A Sticky Navigation Bar on Scroll Using Javascript

In this post, we'll create a super simple navigation bar that sticks to the top of the page even after the user scrolls past it. This solution is incredibly straightforward using vanilla javascript. 

Let's start by creating some HTML structure for the page. We should have a header, followed by some navigation links, followed by some text. If you want to leave out the header and just keep the Nav Links at the top 100% of the time, that will work too. 

<h2>This is a header</h2>

<nav id="navigation">
  <ul>
    <li>These</li>
    <li>Are</li>
    <li>Some</li>
    <li>Nav</li>
    <li>Links</li>
  </ul>
</nav>
<p>Lorem ipsum blah blah blah</p>

Next, let's grab the navigation's nav item and hold it in a constant using Javascript. We also need to establish how far down the page the navigation starts. This is also known as the offset from the top. We can save it as a variable called navTop. 

const nav = document.querySelector('#navigation');
const navTop = nav.offsetTop;

Regardless of how far we scroll down the screen, the navTop will remain the same. But we need to know when to activate our sticky navigation. We can do that by determining how far we've scrolled so far using the window's scrollY property. Let's set up a basic function and event listener and console.log the two properties to see what's happening. 

function stickyNavigation() { 
  console.log('navTop = ' + navTop);
  console.log('scrollY = ' + window.scrollY);
}

window.addEventListener('scroll', stickyNavigation);

Now whenever you scroll the page, you should see some output in the console like this: 

Screenshot 2017-09-16 09.05.25.png

With this information, we can start to grasp what's happening here. The navTop stays constant the whole time. Meanwhile, the scrollY gradually increases as we move down the page. Thus, we'll need to trigger our sticky functionality when scrollY is greater than navTop. 

function stickyNavigation() {
  if (window.scrollY >= navTop) {
    document.body.classList.add('fixed-nav');
  } else {
    document.body.classList.remove('fixed-nav');
  }
}

So what does the 'fixed-nav' CSS class do? It merely defines a fixed position for the element. 

/* CSS code, add in a separate file or style tag */
/* always applied */
nav {
  width: 100%;
  background-color: black;
  text-align: center;
  margin: 0 auto;
}

/* applied after scroll height reached */
.fixed-nav nav {
  position: fixed;
  top:0;
  z-index: 1;
}

Now it may seem like our work here is done, but there's one last thing we can add to polish off this function. You may have noticed that immediately when the fixed position is activated, the text jumps a little bit. We can eliminate that by adding a top padding equal to the height of the navigation bar when the fixed navigation is activated. So our final javascript code snippet looks like this:

const nav = document.querySelector('#navigation');
const navTop = nav.offsetTop;

function stickyNavigation() {
  console.log('navTop = ' + navTop);
  console.log('scrollY = ' + window.scrollY);

  if (window.scrollY >= navTop) {
    // nav offsetHeight = height of nav
    document.body.style.paddingTop = nav.offsetHeight + 'px';
    document.body.classList.add('fixed-nav');
  } else {
    document.body.style.paddingTop = 0;
    document.body.classList.remove('fixed-nav');
  }
}

window.addEventListener('scroll', stickyNavigation);