Design fixed navigation on scroll with CSS and jQuery

Table of Contents

One thing that can increase user experience and overall impression from your website is always present navigation. Why should the visitor scroll back to the top or click one more time to let the script move the window to the top? Remember, every click counts. We can easily solve this problem of accessibility by creating fixed navigation. However, this type of navigation is useless if visitor stays in the top part of the page. Why not to use jQuery to “switch” between static and fixed navigation? This is exactly what we will do today …

The challenge for today is to use CSS and jQuery to create navigation that will switch between static and fixed positioning according to specific condition. This condition will be scrolling the window. So, when user scrolls the page to some degree, the navigation will become fixed. When he comes back to the top, navigation will automatically switch back to static. We will also add a shadow to navigation when it will be fixed so users will be able to distinguish it from the rest of page.

We will not use any external assets except jQuery library. Below are links to live demo on Codepen and also code in Github repository. Feel free to use and modify the code as you want.

Codepen live demo:

https://codepen.io/d3v3r0/pen/Lzfjt

HTML

To make it a bit shorter, let’s suppose that we already have basic HTML valid structure (doctype, html, head and body) in place so we can move straight to today’s challenge. The first thing to create the HTML skelet will be creating a <div> with class ”wrapper”. This will wrap up whole layout of the page. Inside this div will be nested following elements. First is <header> with class “js-header” and role attribute “banner” followed by three <section> elements. These section elements are just to test the scrolling functionality and are not required at all.

The navigation, nested inside header, will be composed of unordered list (<ul>) with class “nav” with four anchor tags (<a>) inside list items (<li>). This all will be wrapped up by <nav> tag with role attribute “navigation”. This is all for HTML.

HTML:

<div class="wrapper">
 <header class="js-header" role="banner">
  <nav role="navigation">
   <ul class="nav">
    <li><a href="#">Home</a></li>
    <li><a href="#">Portfolio</a></li>
    <li><a href="#">About</a></li>
    <li><a href="#">Contact</a></li>
   </ul>
  </nav>
 </header>
 <section></section>
 <section></section>
 <section></section>
</div>

CSS

After setting the HTML, let’s move forward into the CSS. We will start by setting up the styles for basic layout of the page. First, let’s set a default “font-size” and “font-family” for body element. Value will be “16px” for size and “sans-serif” for family. Next is to reset “margin” and “padding” of body and html elements by setting them to “0”. Also, let’s set “width” and “height” to “100%” for these two elements. Then, use the universal selector (*) and create new rule containing “box-sizing” property with value of “border-box”. For “wrapper” div, we will set its “width” and height to “100%”.

CSS:

/*Typography*/
body {font: 16px sans-serif;}
/*Layout*/
html,
body {
 margin: 0;
 padding: 0;
 width: 100%;
 height: 100%;
}
* {box-sizing: border-box;}
.wrapper {
 width: 100%;
 height: 100%;
}

Now we will style the sections and header. This will be fast and simple. Section will have “width” set to “100%”, “min-height” to “480px” and “background” of “#eee” (light grey). Since there is no content in it, you can also use regular “height” property if you want. For header, we will also set the “width” to “100%”. The “background” will be “#fff” (white) and let’s also add “transition” with value of “all .3s” (it will be applied to box-shadow in the future).

CSS:

section {
 width: 100%;
 min-height: 480px;
 background: #eee;
}
/*Navigation*/
header {
 width: 100%;
 background: #fff;
 transition: all .3s;
}

After styling up the header we will move to navigation. Let’s select the “nav” class (unordered list) and set its “display” property to “flex”. To place the navigation links to the center of header, set “justify-content” to “center”. Another values are “flex-start”, “flex-end”, “space-around” and “space-between”. To get rid of white space and bullet points set the “margin” and “padding” to “0” and “list-style-type” to “none”.

CSS:

.nav {
 display: flex;
 justify-content: center;
 margin: 0;
 padding: 0;
 list-style-type: none;
}

The list is OK, next are list items and anchor tags. For list items, set the “padding” to “1em 0” to add some space above and below the links and also set the “width” to “6em” to space the links consistently.

CSS:

.nav li {
 padding: 1em 0;
 width: 6em;
}

Now the links … Let’s set the top and bottom “padding” to “.8em”. This will move bottom border we will create for “hover” state. To make the links stand out a bit, set the “font-weight” to bold and also align them to “center”. Use “text-decoration” with value of “none” to remove underline and set the “color” to “#000” (black). For “hover” state we will change the “color” to “#666” (grey) and add “border-bottom” with value of “.25em solid”. Let’s also add “transition” set to “all .3s” to smoother the transition between normal and “hover” state.

CSS:

.nav a {
 padding: .8em 0;
 font-weight: bold;
 text-align: center;
 text-decoration: none;
 color: #000;
 transition: all .25s;
}
.nav a:hover {
 color: #666;
 border-bottom: .25em solid;
}

Last thing in CSS will be creating a special class that will be appended by jQuery to header and change it to fixed. This class will be called “fixed” and will also contain “box-shadow” with value of “0 2px 5px #ccc” (horizontal vertical spread color).

CSS:

.fixed {
 position: fixed;
 box-shadow: 0 2px 5px #ccc;
}

jQuery

HTML and CSS are set and ready, so the last thing that remains is to put together the jQuery code to finish this challenge. We will start by creating a function that will contain all the code and will run after the page is loaded. You can either use “$(function() {})” or “$(document).ready(function() {})”. Both of these approaches have the same effect – code inside will run after the whole page (document) is loaded.

jQuery:

$(function() {});

Inside this function we will select the “window” object and jQuery “scroll()” method to monitor for scrolling event and it will use handler in the form of function to execute.

jQuery:

$(function() {
 $(window).scroll(function() {});
});

Now we will create local variable “winOffset” to store the offset value. Because various browsers can have two different names for document object will also use “||” (or) operator to cover both of the options. We will select the “document” and “body” and use “scrollTop” property to find out what the offset is (scrollTop will return an integer).

jQuery:

var winOffset = document.documentElement.scrollTop || document.body.scrollTop;

It is time to create the heart or engine of the mechanism. It will be based on “if” statement. The condition will be if value of winOffset variable is bigger then “50” (height of header). When true will be returned, we will select the “header” via “js-header” class and use “addClass()” jQuery method to append class “fixed” to it. For case of false we will use similar code, but instead of “addClass()” method “removeClass()” will be used, also with “fixed” as parameter.

jQuery:

if(winOffset > 50) {
 $('.js-header').addClass('fixed');
} else {
 $('.js-header').removeClass('fixed');
}

With this setting, whenever will user scroll more than 50 pixels from top (height of header), header will switch fixed position, otherwise it will switch to static.

Whole jQuery code:

$(function() {
 $(window).scroll(function() {
  var winOffset = document.documentElement.scrollTop || document.body.scrollTop;
  if(winOffset > 50) {
   $('.js-header').addClass('fixed');
  } else {
   $('.js-header').removeClass('fixed');
  }
 });
});

Summary

That’s it! Our goal was achieved and the challenge was successfully solved. I hope you enjoyed this tutorial, learned something new or at least dust off your CSS and jQuery skills. AD

If you liked this article, please subscribe so you don't miss any future post.

If you'd like to support me and this blog, you can become a patron, or you can buy me a coffee 🙂

By Alex Devero

I'm Founder/CEO of DEVERO Corporation. Entrepreneur, designer, developer. My mission and MTP is to accelerate the development of humankind through technology.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.