By Albert Senghor

Create an Animated Sticky Navigation Menu with Vanilla JavaScript

By Albert Senghor

When adding a navigation menu to a webpage, there are a lot of things to take into consideration. For example where to position it, how to style it, how to make it responsive. Or maybe you want to add some kind of animation to it (tastefully, of course). At this point you might be tempted to grab a jQuery plugin which does most of that for you. But that needn’t be! It’s actually pretty simple to create your own solution in a few lines of code.

In this post, I will demonstrate how to create an animated, sticky navigation menu with vanilla JavaScript, CSS and HTML. The final product will slide up out of your way as you scroll down the page, then slide back into view (with a stylish see-through effect) when you scroll back up. This is a technique used by such popular sites, such as Medium and Hacker Noon.

After reading you’ll be able to employ this technique in your own designs, hopefully to great effect. There’s a demo at the end of the article for the impatient.

Sticky Navigation Menu: Basic HTML Structure

The following is the skeleton HTML code that we will be working with. Nothing too exciting going on here.

<div class="container">
  <div class="banner-wrapper">
    <div class="banner">
      <div class="top">
        <!-- Navbar top row-->
      <div class="nav">
        <!-- Links for navigation-->

  <div class="content">
    <!-- Awesome content here -->

Applying a Little Styling

Let’s add some styling to the main elements.

Main Container

We’ll need to remove any inherent browser styles and set the width of our container to 100%.

  padding: 0;
  margin: 0;

  width: 100%;

Banner Container

This is a wrapper around the navigation menu. It is always sticky and slides to hide or reveal the navigation menu as you scroll your page vertically. We are giving it a z-index value to ensure that it appears on top of the content.

.banner-wrapper {
  z-index: 4;
  transition: all 300ms ease-in-out;
  position: fixed;
  width: 100%;

Banner Section

This contains the navigation menu. Changes in position and background color are animated through the CSS transition property when a page is scrolled up or down.

.banner {
  height: 77px;
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  background: rgba(162, 197, 35, 1);
  transition: all 300ms ease-in-out;

Content Section

This section will contain a background image and text. We will add a parallax effect to this part of the page in a later section of the article.

.content {
  background: url( center no-repeat;
  background-size: cover;
  padding-top: 100%;

Animating the Menu

The first thing we need to do is to attach an event handler to the scroll event, so that we can show and hide the menu accordingly when the user scrolls. We’ll also enclose everything in an IIFE so as to avoid clashes with other code running on the same page.

(() => {
  'use strict';

  const handler = () => {
    //DOM manipulation code here

  window.addEventListener('scroll', handler, false);

Setting Some Initial Values

We’ll be using a refOffset variable to represent the amount of distance the user has scrolled down the page. This is initialized to 0 on page load. We’ll use a bannerHeight variable to store the menu’s height and will also need references to the .banner-wrapper and .banner DOM elements.

let refOffset = 0;
let visible = true;
const bannerHeight = 77;

const bannerWrapper = document.querySelector('.banner-wrapper');
const banner = document.querySelector('.banner');

Establishing Scroll Direction

Next we need to establish scroll direction so that we can show or hide the menu accordingly.

We’ll start off with a variable called newOffset. On page load this will be set to the value of window.scrollY — the number of pixels that the document is currently scrolled vertically (so 0 initially). When a user scrolls, newOffset will increase or decrease accordingly. If it is greater than the value stored in bannerHeight then we know our menu has been scrolled out of view.

const newOffset = window.scrollY;

if (newOffset > bannerHeight) {
  // Menu out of view
} else {
  // Menu in view

Scrolling down will make newOffset greater than refOffset and the navigation menu should slide up and disappear. Scrolling up will make newOffset less than refOffset and the navigation menu should slide back into view with a see through effect. After performing this comparison, we will need update refOffset with the value of newOffset to keep track of how far the user has scrolled.

if (newOffset > bannerHeight) {
  // Menu out of view
  if(newOffset > refOffset) {
    // Hide the menu
  } else if (newOffset < refOffset) {
    // Slide menu back down

  refOffset = newOffset;
} else {
  // Menu in view

Animating the Menu

Finally, let’s add an animateIn and an animateOut method to show and hide the menu. We should also make sure that the see-through effect is removed from the menu once the top of page is reached.

if (newOffset > bannerHeight) {
  if (newOffset > refOffset) {
  } else {

  refOffset = newOffset;
} else { = 'rgba(162, 197, 35, 1)';

And here are the functions that do the animation.

function animateOut() { = `translateY(-${bannerHeight}px)`; = `translateY(-${bannerHeight}px)`; = `translateY(-${bannerHeight}px)`; = `translateY(-${bannerHeight}px)`; = 'rgba(162, 197, 35, 0.6)';

function animateIn() { = 'translateY(0px)'; = 'translateY(0px)'; = 'translateY(0px)'; = 'translateY(0px)'; = 'rgba(162, 197, 35, 0.6)';

You might want to consider using a CSS class for this, which you could remove and apply accordingly. In this case however, I have gone with the JavaScript version.


Here is a demo of the working menu.

See the Pen ZKJVdw by SitePoint (@SitePoint) on CodePen.


This article has described how you can design an animated navigation menu in a few lines of code using just vanilla JavaScript — no jQuery needed. The menu slides to disappear when you scroll down and slides back into view with a transparency effect when you scroll back up. This is done by monitoring the vertical scroll direction and applying CSS transformations to the DOM element as required. Such a custom solution gives you more freedom to easily and flexibly design according to your own requirements and specifications.

Want to up your JavaScript skills? Check out our courses Introduction to JavaScript and
JavaScript: Next Steps

This article was peer reviewed by Vildan Softic. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!

  • M⃠ ⃠S⃠ ⃠i⃠ ⃠N⃠ ⃠L⃠u⃠n⃠d⃠


    Would there be any downside to put the animation-css among the rest of the css and just swap class-names in js instead?

    • Albert Senghor

      Thanks. You are absolutely right. The path I have chosen allows me to include dynamically generated dimensions in pixels, in my js code.

      • Yimiprod

        But why using pixel dimensions? You could have use precent value for translateY

        • Albert Senghor

          I have used dimensions in pixels in order to reliably set required nav menu height. This height is used to determine with precision how far the nav menu will slide up/down.

  • Good tutorial! I always like articles that stick to vanilla JS.

    • Albert Senghor

      Thanks James!

    • This is a shit tutorial..

      • James Hibbard

        Why? If you have any kind of constructive criticism, we’ll gladly hear it.

  • Tom Livingston

    Nice, but my initial thought was ‘why would anyone hide their nav?’.

    • Albert Senghor

      Thank you! The navigation menu is hidden in order to get more space for the content.

  • Albert Senghor

    Thank you for the alternative solution!

  • Welcome to 2013. Why the hell you should make a keyframe animation with javascript? You are in 2017, and there is aria-attributes for UI-related components change. Just add/remove the attribute and let CSS do his work..

    PS: And you don’t need prefixes anymore and you don’t need a px height, just use translateY(-100%).

  • Camilo Reyes

    Beautiful, thanks for sharing.

    TBH, it makes me sad to see piles of JS dependencies to do this exact same feat.

  • surja

    Thank you for the article.

  • James Hibbard

    Hi, we consider translation requests on a case-by-case basis. Email to ask about translating this article. You can find more information here:

  • Walid Bizid

    Great job

Get the latest in JavaScript, once a week, for free.