JavaScript 30 - Day 24 (Sticky Nav)

This is day 24 in my #javascript30 journey. This is the free course from Wes Bos that lets you brush up on your JavaScript skills via 30 projects.

Yesterday we created a speech synthesis generator. You can keep track of all the projects we're building here.

Today we're solving the age old problem of a sticky nav bar.


Day 24 - Sticky Nav

Today we are creating a nav bar that is placed staticly below a header image. Then as the user scrolls down and the navbar hits the top of the screen it should transition to a fixed location at the top of the browser.

Now that we know what we want to achieve we can get started.

To begin we will need to select the navbar. We will also need to add a function that will run on every scroll event:

const nav = document.querySelector('#main')

function fixNav() {  
  console.log('The window was scrolled')
}

window.addEventListener('scroll', fixNav)  

Next we're going to calculate the navbar location on page load. This will give us the exact pixel location of the top of the navbar:

const topOfNav = nav.offsetTop  

Now that we have this calculation we can add some logic into our fixNav function that is running on every scroll event. Our logic will add a new class to the body if the window is scrolled at or below the top of the navbar.

function fixNav() {  
  if (window.scrollY >= topOfNav) {
    document.body.classList.add('fixed-nav')
  } else {
    document.body.classList.add('fixed-nav')
  }
}

We are adding this class to the body so that we can pass this additional property down to any children that we need.

To make any visual changes happen we will need to update the style sheet:

.fixed-nav nav {
  position: fixed;
  box-shadow: 0 5px rgba(0, 0, 0, 0.1);
}

This is working but when the scroll hits the top of the navbar it causes a re-render of the rest of the page. During this re-render the rest of the content jumps up to compensate for the navbar no longer pushing it down (as fixed elements don't take up space).

It gives us this jumpy look:

Content jumps up

To combat this we need to add an offset to the body content:

function fixNav() {  
  if (window.scrollY >= topOfNav) {
    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')
  }
}

Now this is working smoothly.

Transition the Logo in on Scroll

An additional nicety that we are adding is the logo to transition in when the navbar becomes fixed.

Currently, our logo styling is set to:

li.logo {  
  max-width: 0;
  overflow: hidden;
  background: white;
  transition: all 0.5s;
  font-weight: 600;
  font-size: 30px;
}

Notice the max-width and overflow? We're going to alter these as we flow down the page. This will be triggered when the fixed-nav class is applied to the body:

.fixed-nav li.logo {
  max-width: 500px;
}

This looks great:
Incoming logo

Subtle Body Scale

The final touch that we are adding to our site is a subtle increase of the site-wrap class.

Again, we are using the fixed-nav class that has been placed on the body to action this change:

.site-wrap {
  ...
  transform: scale(0.98);
  transition: transform 0.5s;
}

.fixed-nav .site-wrap {
  transform: scale(1);
}

This very subtle update in size is all that's needed to give a nice finished feel to this site:

Finished


Now we have a sticky nav bar created in all vanilla JavaScript. Yay!

You can view this in action here.

You can keep track of all the projects in this JavaScript30 challenge here.