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

The postMessage function in my Javascript SlackBot seems to be flooding the channel with messages

I am currently setting up a Slack bot using JavaScript combined with several useful libraries. The main function of this bot is to execute a postMessageToChannel method whenever a user in the channel mentions the specific keyword "help." However, I am fa ...

Tips for editing events in the "react-big-calendars" component

I am looking to implement a feature where users can click on events in a calendar and then edit either the dates or event titles. Can this functionality be achieved using "react-big-calendar"? If not, are there any other packages you can recommend? <Cal ...

Adjust the div container to wrap underneath the image if the webpage width decreases

Hello there, I am currently in the process of creating my own portfolio. I have a brief introduction that I would like to display next to an image of myself, with the image positioned on the right side. I have a div called "container" which contains anoth ...

Save a JSON object from a URL into a JavaScript variable using jQuery

I am trying to figure out how to use the .getJSON() function to store a JSON object as a JavaScript variable. Can someone guide me through this process? var js = $.getJSON(url, function(data) {...} ); Is it necessary to include the function(data) { ...

Is it possible to prefetch library calls in Next.js in advance?

Recently, I started working with GeoChart using react-google-charts (https://github.com/RakanNimer/react-google-charts). However, I noticed that several scripts load after the entire process is completed. In my scenario, is loading towards the end. Is t ...

tips for sending a chosen item to the Vujes API

How can I send the selected item from a dropdown to an API in Vue.js? <select :v-model="selectedRole" class="custSelect"> <option v-for="option in options" v-bind:value="option.value"> {{option.role}} </option> ...

Leveraging the ng-hide property of one controller to adjust the ng-style attribute of another controller through a centralized global controller

Seeking assistance with accessing the boolean value of ng-hide from one controller in order to alter CSS properties of another controller utilizing a global controller. Here is the jsfiddle link: https://jsfiddle.net/dqmtLxnt/ HTML <div ng-controller= ...

What makes 'Parsing JSON with jQuery' unnecessary?

Just performed an ajax request with a query and noticed that my response is already in the form of a JavaScript object. When I try to parse the JSON using: var obj = jQuery.parseJSON(response); 'obj' turns out to be null, yet I can directly ac ...

Making a div equal in height to an image

What I currently have is like this... https://i.stack.imgur.com/7FeSi.png However, this is what I am aiming for. https://i.stack.imgur.com/fn9m0.png The image dimensions are set up as follows... #content img{ padding: 3%; width: 60%; height: 50%; floa ...

Clear all CSS styles applied to a targeted division

My website built on Wordpress links to several CSS files. One specific div has its own unique style that is separate from the main Wordpress styles. Unfortunately, the Wordpress CSS ends up interfering with my custom div's layout. Is there a way in HT ...

My initial junior UI/UX assignment: Can you confirm whether this form modal dialog is pixel-perfect, and offer any suggestions for improvements

Currently diving into my first project as a Junior UX/UI Designer. Coming from a background in software engineering, I decided to challenge myself by focusing on design. I'm seeking feedback on the pixel perfection of this modal window, which my seni ...

Backbone "recalling" stored data in attributes

Presented here is a basic model: myTestModel = Backbone.Model.extend({ defaults: { title: 'My Title', config: {}, active: 1, } }) While nothing particularly stands out, there is an interesting observation regardi ...

Tips for retrieving the xpath of elements within a div using python selenium

<div class="col-md-6 profile-card_col"> <span class="label">Company Name :</span> <p class="value">Aspad Foolad Asia</p> </div> For a while now, I've been trying to troublesho ...

What is the correct way to maintain focus on a nested scrolling div over another scrolling div with jQuery?

I have encountered an issue with a modal view that contains content extending beyond its boundaries. To remedy this, I applied the overflow-y: scroll property. However, the problem arises when the modal view overlaps with the body, which also has scrolling ...

Implementing form validations using JavaScript for a form that is created dynamically

I have dynamically created a form using javascript and now I need to add mandatory validations on the form. The validations should trigger when the dynamically created button is clicked. However, I am facing an issue where I receive an error whenever I t ...

When the dropdown form options in HTML are clicked, they appear disproportionately large compared to the initial select box

Sorry if my wording is a bit off, I'm still relatively new to working with CSS and HTML. I've encountered an issue where the dropdown options appear much larger than the select box itself when clicked. I can't seem to figure out how to fix i ...

Acquiring the underlying code of websites within the local network

In the tool I am creating, it takes a URL as a parameter and retrieves the source code from that URL for analysis. The tool will be hosted on our domain, with the capability of analyzing web pages of sites within our internal network domain. Here is the Jq ...

What is the best way to dynamically change the content of a div based on user selection?

When a user selects an option in the HTML, I want to display another div set to Block. I tried putting the OpenAskuser() function in a button, but it didn't work either. It would be ideal if we could achieve this without a button. Just have the displ ...

Using the identical code, Wicked PDF generates distinct reports on separate computers

While utilizing Ruby on Rails to render a PDF using WickedPDF and sending it via email, I encountered an unexpected issue. The same code is present on two separate computers, both with up-to-date versions synced from GitHub. However, the generated PDF repo ...

techniques for utilizing dynamic variables with the limitTo filter in AngularJS

<div class="container"> <div class="row" ng-repeat="cmts in courCmt.argument" ng-init="getUserInfo(cmts)"> <div class="col-sm-1 col-xs-2"> <div class="thumbnail"> &l ...