To prevent flickering when using child flex elements, it's best to use position fixed along with a dynamically updated scrollbar position using Javascript

My navigation component includes a slim banner that should hide upon scroll and a main-nav bar that should stick to the top of the screen and shrink when it does so.

I initially looked into using Intersection Observer based on a popular answer found here: Event to detect when position:sticky is triggered

However, I encountered an issue where my child elements (which are flex) caused a flicker when transitioning between showing and hiding the sticky banner. Removing these child elements isn't an option, so I am considering applying position: fixed to the main-nav with top: 40px. This setup allows the skinny-banner to scroll away as intended, but now I am seeking help in obtaining the scrollbar position via JavaScript. Once achieved, I plan to add a class like .isSticky when the skinny-banner disappears to ensure that the main-nav sticks to the top.

.isSticky {
 top: 0;
 height: 66px;
}

body { 
 margin: 0;
 height: 200vh;  
}

.skinny-banner{
  background: lightblue;
  height: 40px;
  display: flex;
}

.nav-menu {
  display: flex;
}

.sticky-nav{
  position: fixed;
  top: 40px;                    

  background: salmon;
  transition: .1s;
}

/* styles for when the header is in sticky mode */
.sticky-nav.isSticky{
  top: 0;
  height: 66px;
}
<header>
   <div class="skinny-banner">Skinny banner that on scroll down disapears.</div>
   <div class="sticky-nav">Sticky Header that on scroll down sticks to top and shrinks in height when stuck</div>
</header>

I would like to develop a vanilla JS, HTML, & CSS solution while preserving the existing HTML structure which includes a wrapping container with the skinny-banner and nav-menu as children.

Answer №1

One of the reasons why the .sticky-nav element is not becoming sticky is because it is nested inside the <header> element, which by default has a display: block property. This default behavior of the <header> element interferes with the sticky positioning of its child elements, including position: sticky. To overcome this issue, you need to set the display: initial for the <header> element.

You can also monitor when the element becomes sticky by observing the value of .offsetTop. This value will change once the element becomes sticky. However, before that, ensure you have specified position: sticky and top: 0 for the .sticky-nav element.

const stickyNav = document.querySelector('.sticky-nav');
const initalPos = stickyNav.offsetTop;

window.addEventListener("scroll", () => {
  if(stickyNav.offsetTop > initalPos) {
    stickyNav.classList.add('isSticky');
  } else {
    stickyNav.classList.remove('isSticky');
  }
});
body { 
 margin: 0;
 height: 200vh;  
}
header{
  display: initial;
}
.skinny-banner{
  background: lightblue;
  height: 40px;
  display: flex;
}

.nav-menu {
  display: flex;
}

.sticky-nav{
  position: sticky;
  top: 0;
  background: salmon;
  min-height: 1px; /* custom this */
  transition: min-height ease 1s; /* custom this */
}

/* styles for when the header is in sticky mode */
.sticky-nav.isSticky{
  top: 0;
  min-height: 66px;  /* custom this */
}
<header>
   <div class="skinny-banner">Skinny banner that, on scroll down, disappears.</div>
   <div class="sticky-nav">Sticky Header that, on scroll down, sticks to top and shrinks in height when stuck</div>
   <div>Test content 1</div>
   <div>Test content 2</div>
   <div>Test content 3</div>
   <div>Test content 4</div>
   <div>Test content 5</div>
   <div>Test content 6</div>
   <div>Test content 7</div>
   <div>Test content 8</div>
   <div>Test content 9</div>
   <div>Test content 10</div>
   <div>Test content 11</div>
   <div>Test content 12</div>
   <div>Test content 13</div>
   <div>Test content 14</div>
   <div>Test content 15</div>
   <div>Test content 16</div>
</header>

Answer №2

Appreciate your valuable insights and sharing of experiences! Here is a solution that you might find useful.

  window.onscroll = function() {stickyNav()};

  function stickyNav() {
    let navbar = document.querySelector(".sticky-nav");
    let banner = document.querySelector(".skinny-banner");      
    let sticky = navbar.offsetTop;
    if (window.pageYOffset >= sticky) {
      navbar.classList.add("isSticky");
  banner.classList.add("hide-banner"); 

    } else {
      navbar.classList.remove("isSticky");
      banner.classList.remove("hide-banner");
    }
  }
    body { 
      margin: 0;
      height: 200vh;  
    }

    .skinny-banner {
      background: lightblue;
      height: 40px;
      display: flex;
    }

    .nav-menu {
      display: flex;
    }

    .sticky-nav {
      position: fixed;
      top: 0px;
      background: salmon;
      transition: .1s;
    }

    /* styles for when the header is in sticky mode */
    .sticky-nav.isSticky {
      top: 0;
      height: 77px;
    }

    .hide-banner {
    display: none;
     }

    div {
      padding: 20px;
      background-color: #f2f2f2;
      border: 1px solid #ddd;
      margin: 10px;
    }
<div class="sticky-nav">Sticky Header that on scroll down sticks to top and shrinks in height when stuck</div>
<div class="skinny-banner">Skinny banner that on scroll down disapears.</div>      
<div>Test Content</div>
<div>Test Content</div>
<div>Test Content</div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best approach to transform an HTML textarea value into JSON format?

Client .. inserting some content into a form <textarea name="data"></textarea> After typing the following data into the textarea: {title: 'Hello one!', author: 'someone'} {title: 'Hello two!', author: 'mygf ...

Is there a way to identify the ID of a button using Javascript specifically in Internet Explorer and Safari browsers?

Within my code, there lies a directive that contains the attribute on-blur = blurFunc($event). Imagine this scenario: I interact with a button bearing the id "myButton" located outside of the directive. How can I determine which specific button was clicke ...

multiple urls causing duplication of states in angular ui routes

Currently, I am faced with an issue while using angularjs in combination with angular ui routes. The problem arises when dealing with multiple URLs for a single route. I have a state named "bookDetails" which corresponds to a book that has a unique id an ...

The integration of CSS into an HTML file is ineffective

I'm new to learning about html and css files, and I've been struggling with a particular issue for some time. I created these files, but I can't seem to apply the css to the html file: app.py from flask import Flask from flask import rende ...

Displaying an iFrame with a height that excludes the header section

My website contains an iframe with a height of 100% and a div positioned above it with a height of 70px. The issue is that the iframe overflows the page, causing the scrollbar to extend beyond the visible area. I am looking for a solution to adjust the ifr ...

Conceal a different CSS class if a certain CSS class is present on the page

I am currently working on organizing my page to distinguish between purchased and unsold products. For the items that have been bought, I am using the CSS class "winning_bid" within a div element. My goal is to hide another div with the class "price" if th ...

What code can I use to prompt clients to refresh JavaScript files automatically?

We are experiencing an issue where, even after pushing out updates with new JavaScript files, client browsers continue to use the cached version of the file and do not display the latest changes. While we can advise users to perform a ctrlF5 refresh during ...

Extracting a precise data point stored in Mongo database

I have been struggling to extract a specific value from my MongoDB database in node.js. I have tried using both find() and findOne(), but I keep receiving an object-like output in the console. Here is the code snippet: const mongoose = require('mongoo ...

Implementing the row delete function in MUI DataGrid using JavaScript

My grid has a delete icon button in each row, but it's not deleting the row. Can someone please help me with this issue? I'm working on a todo app where each row should have its own delete button. I'm struggling to connect the deleteTodo co ...

How can I handle pings in Discord using discord.js?

I've been working on a feature in my discord.js bot that responds to pings, but I'm running into issues. Even after trying <@BOTID> and @BOT#0000, the functionality is not behaving as expected. Here's the snippet of code I'm using ...

Utilizing IndexedDB for data storage

Hey there! I am currently working on storing three fields in an IndexedDB. When I view them in the browser, I see the names of each index - content, content2, and content3. However, only data is being saved into content3. Can you help me figure out why? B ...

Executing PHP scripts using Ajax

Check out the code snippet below: <?php //echo $this->Html->css(array('bootstrap', 'mark', 'style')); echo $this->Html->script(array('timer','swfobject','bootstrap.min.js')); // ...

Determine the precise boundaries of the React component

I am working with a basic ellipse element: <span style={{ width: /*someWith*/, height: /*someHeight*/, borderRadius: "50%" }}/> and, I am using getBoundingClientRect() to retrieve its bounds (displayed in blue). https://i.ssta ...

Navigating to two separate webpages concurrently in separate frames

I am working on creating a website with frames, where I want to set up a link that opens different pages in two separate frames. Essentially, when you click the link, one page (such as the home page in a .html file) will open in frame 1 on the left side, ...

The pure css overlay does not display correctly on Chrome

Could you take a look at the link provided below (just widen the result window a bit) and let me know why there are white lines on top and on the sides of the boxes when the hover overlay is fully loaded? Even when I use a background image for those divs, ...

"Troubleshooting VueJS: Issue with default value not being set in

Trying to set a default value for a select in Vue. <script src="https://unpkg.com/vue"></script> <div id="app"> <select v:model="select"> <option value="1">1</option> <option value="2">2</optio ...

What are some alternative solutions when functional component props, state, or store are not being updated within a function?

Initially, I need to outline the goal. In React frontend, I display data that aligns with database rows and allow users to perform CRUD operations on them. However, in addition to actual database rows, I include dummy rows in the JSON sent to the frontend ...

Warning: The core schema has detected an unknown property `color` for the component or system `undefined` in Aframe + Vuejs. This issue was flagged within 10 milliseconds in

I am facing some challenges trying to integrate Aframe and vuejs seamlessly, as the console is displaying warning messages. It seems like Aframe is validating the attribute values before vue has a chance to modify them. Warning messages core:schema:warn ...

What's Going on with My Angular Dropdown Menu?

Snippet of HTML code: <li class="nav-item dropdown pe-3"> <a class="nav-link nav-profile d-flex align-items-center pe-0" (click)="toggleProfileMenu()"> <img src="assets/img/profile-img.jpg" alt=& ...

The content is being pushed down by the responsive navigation bar

While the navbar is in normal non-responsive mode (I'm not sure if that's the right terminology) on Chrome and you scroll down, the logo image stays behind the menu. However, when the screen width shrinks and the menu collapses, clicking the icon ...