DEV Community

Cover image for The World's Easiest Collapsible Nav Bar
David Rodriguez
David Rodriguez

Posted on

The World's Easiest Collapsible Nav Bar

"How to make a collapsible nav bar with hamburger icon"

"How to make hamburger bar"

"Easy vanilla JS responsive navigation menu"

If these searches look familiar to you - and the disappointment that follows them is a feeling you're well acquainted with - then chances are, you're a newer coder who's looking to go mobile-responsive. 

If we want users to stick around, we'll need our website to look gorgeous and work intuitively in a clean, organized way on ALL device sizes. If you've not lived under a rock for the last 10 years, then you've undoubtedly seen the "Holy Grail" of mobile-friendly features, the collapsible nav bar - sometimes referred to as a "Hamburger bar" for the fact that the most common icon we use to indicate it's presence is, in fact, shaped kind of like a hamburger.

The idea is simple.
When our app is displayed on a mobile device with a screen below a set size, our normal web page navigation bar, which might look something like this:
Desktop view of a bootcamp website

…will instead disappear and be replaced by this:
Mobile view of the same header - note that the icon is not a hamburger. You can use any icon you want! Just make sure users will intuitively understand what it's for!

Now our navigation bar won't take up the entire screen. When a user taps on that icon, our navigation bar should roll out smoothly below to display the list items - our links - in a column below.


So how do we do this? There are a dozen ways, probably, but I believe simplicity is the sturdiest foundation for quality engineering, so let's start with a very basic html template that can easily be built out later as needed:

<!DOCTYPE html>
<html>
  <head>

    <link rel="stylesheet" type="text/css" href="styles.css" />
    <link
      rel="stylesheet"
      href="https://fonts.googleapis.com/icon?family=Material+Icons"
    />
  </head>
  <body>
    <nav>
      <div class="hamburger">
        <i class="material-icons">menu</i>
      </div>
      <ul class="nav-links">
        <li><a href="index.html">Home</a></li>
        <li><a href="page2.html">About</a></li>
        <li><a href="somebody@someplace.com">Contact</a></li>
      </ul>
    </nav>
    <script src="script.js"></script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

As you can see, it's pretty straightforward - but just in case, here's a breakdown of the code itself:

<!DOCTYPE html>
<html>
  <head>
    <!-- This will link our stylesheet to our index.html: -->
    <link rel="stylesheet" type="text/css" href="styles.css" />
    <!-- This will link the google icon for our hamburger bar 
      so we have access to use it -->
    <link
      rel="stylesheet"
      href="https://fonts.googleapis.com/icon?family=Material+Icons"
    />
  </head>
  <body>
    <!-- Here we create our nav bar element -->
    <nav>
      <!-- and then give it a div to contain our icon and a class 
        name to use in our css to make it disappear and 
        reappear at will! -->
      <div class="hamburger">
        <!-- This is where we're sticking the icon itself -->
        <i class="material-icons">menu</i>
      </div>
      <!-- OUTSIDE of our hamburger icon's div container, we want 
        to place our unordered list of links that make up our nav bar 
        itself, and give it a different class - this is how we'll be 
        able to set up our javaScript function to listen for a click 
        and then display the hamburger bar ONLY based on media query, 
        and separately display the nav bar in association with our e
        vent listener, which will check for the click event happening 
        on the hamburger bar, and toggle our nav bar display on or 
        off, depending! -->
      <ul class="nav-links">
        <!-- our unordered list items - the links themselves -->
        <li><a href="index.html">Home</a></li>
        <li><a href="page2.html">About</a></li>
        <li><a href="somebody@someplace.com">Contact</a></li>
      </ul>
    </nav>
    <!-- and that's it! Then we just link to our script.js - remember, 
      this should go at the end of the body to ensure that no scripts 
      are trying to run before the element they are attached to 
      actually exists! 👍 -->
    <script src="script.js"></script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Great! So we have the "skeleton" of our nav bar. Let's flesh it out and make it it mobile-friendly, shall we? Enter - CSS!

.hamburger {
  display: none;
}

.nav-links {
  display: flex;
}

@media screen and (max-width: 768px) {
  .nav-links {
    display: none;
    flex-direction: column;
  }

  .hamburger {
    display: block;
  }
}
Enter fullscreen mode Exit fullscreen mode

It doesn't get much easier than that. Here's what we have going on in that sample style.css file:

/* First we set up our desktop view - remember, 
  our icon is only meant to show on mobile, so 
  we have set it's "display" property to have a 
  value of "none" by default. */
.hamburger {
  display: none;
}
/* then we make sure our nav links will "flow" correctly
 by setting display to "flex" */
.nav-links {
  display: flex;
}
/* media query time! We're selecting anything that 
has a max width of 768px - this can be anything you want,
but generally you should choose the width of the most popular
mobile devices at the time, or mobile standards, like iPhone
9 or above or Samsung S20 or above. */
@media screen and (max-width: 768px) {
  /* now we set the nav links to display "none" - or dissappear 
  by default - every time the screen size is a mobile size. */
  .nav-links {
    display: none;
    /* we also want to make sure they now "flex" into a column 
    when they do display; */
    flex-direction: column;
  }
/* ...and our hamburger icon now defaults to be displayed as a block
rather than display "none", as it does on desktop view */
  .hamburger {
    display: block;
  }
}
/* The net result is that there is now a class which makes the icon display 
  when we're in mobile view, and a class which makes the nav bar disappear 
  in mobile view. */
Enter fullscreen mode Exit fullscreen mode

We're almost there, believe it or not!

So now we have a desktop view that shows no hamburger icon but does show our nav links in a nice smooth row, and we have a mobile view where the hamburger icon DOES show up, and the nav links disappear instead - and if the nav links DO appear, they'll be in a column.
That just leaves the magic of change - of ACTION! Now we visit the world of JavaScript to power this magic:

var hamburger = document.querySelector('.hamburger');
var navLinks = document.querySelector('.nav-links');

hamburger.addEventListener('click', function () {
  navLinks.style.display = navLinks.style.display === 'none' ? 'flex' : 'none';
});
I know. It CAN'T be that easy, right?
It really, really is.

Let's break this down:
// Because everything we're messing with is html, we need to give it a
// name, so-to-speak, over here in the world of js. We can do that by
// creating a variable to house each element we want to manipulate,
// and assigning the value as being basically, "the result of querying
// the DOM and retrieving any element matching X", where "X" is the name
// of the class or id we gave that element. Check it out:

// our hamburger icon:
var hamburger = document.querySelector('.hamburger');

// and our nav bar itself:
var navLinks = document.querySelector('.nav-links');

//  now we just add an eventlistener that listens to the hamburger icon
//  to receive a "click" event.
hamburger.addEventListener('click', function () {
  // every eventListener is built by default to accept two parameters - 
  // the event that it's listening FOR, and a "callback function", or a 
  // function it fires off in response to "hearing" the event occur. When 
  // this eventListener in particular "hears" a "click" on our "hamburger" 
  // element, it fires off this function, which is a terniary statement.

  navLinks.style.display = navLinks.style.display === 'none' ? 'flex' : 'none';
  // This teriary is basically an if-else statement (like all terniearies are)
  // and essentially says, "ReAssign the value of the 'display' attribute of
  // navLinks' style based on the result of the following assessment:
  // IF the value of the display property AT THAT TIME is currently set to
  // "none", change it to be "display: 'flex'.
});
Enter fullscreen mode Exit fullscreen mode

And yes - that REALLY IS IT!

By incorporating mobile optimization standards like these, you’ll unleash the power of your apps on ANY device — the possibilities for specialization from here are endless, so start simple, yes… But then go play!

The world is your responsive web-designed oyster! 😁

Top comments (0)