Tutorials Web Development

Responsive Sliding Drawer Menu with Lightbox Effect

1/5 (1)

One popular navigation trend is the hidden sliding menu for large and small screen displays. Some may call this a hamburger menu, side-toggle menu, sliding drawer menu, or anything similar - but they all describe the same interface. The menu is naturally responsive and saves on screen real estate by hiding the navigation when you don't need it.

Live Demo - Download Source Code

I recently found an extraordinary example on the Comedy Central website employing a dynamic sliding menu. In this tutorial I want to demonstrate how to build a similar interface using jQuery. The menu will open and create a lightbox effect to hide the main page content so visitors will focus solely on the navigation. My demo is fully responsive and should work on any screen size.

Building the Webpage

To get started I've created a blank HTML document along with an external stylesheet. Also I've grabbed a local copy of the jQuery library to include in the page.

1
2
3
4
5
6
7
8
9
10
11
12
13
<!doctype html>
<html lang="en-US">
<head>
  <meta charset="utf-8">
  <meta http-equiv="Content-Type" content="text/html">
  <title>Responsive Hamburger Toggle Menu - Template Monster Demo</title>
  <meta name="author" content="Jake Rocheleau">
  <link rel="shortcut icon" href="http://static.tmimgcdn.com/img/favicon.ico">
  <link rel="icon" href="http://static.tmimgcdn.com/img/favicon.ico">
  <link rel="stylesheet" type="text/css" media="all" href="css/styles.css">
  <script type="text/javascript" src="js/jquery-1.11.1.min.js"></script>
  <script type="text/javascript" src="js/hamburger-menu.js"></script>
</head>

But the real important stuff is located within the page body. Since we need a lightbox/shadowbox to cover the page I'm wrapping everything into a container with the ID #pgcontainer. This will be useful when sliding the entire page over to make room for the menu.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<body>
<div id="pgcontainer">
  <header>
    <div id="navbar">
      <a href="#" class="menubtn">menu</a>
      <!-- use captain icon for toggle menu -->
      <div id="hamburgermenu">
        <ul>
          <li><a href="#">Homepage</a></li>
          <li><a href="#">About the Site</a></li>
          <li><a href="#">Basic History</a></li>
          <li><a href="#">Our Products</a></li>
          <li><a href="#">Contact</a></li>
        </ul>
      </div>
    </div>
    <div class="overlay"></div>
  </header>
  <div id="content">
  <!-- filler content -->
  </div>
</div><!-- @end #pgcontainer -->
</body>

Notice the page container even holds the navigation header along with the sliding menu #hamburgermenu. A bit of padding along with the .overlay div will ensure the menu is still clickable and located above the lightbox.

The nav links are kept in an unordered list and you could even include other bits of HTML as well. Everything located within #hamburgermenu will display to the left side when the menu opens. Otherwise it stays hidden using a 0px width.

Also the only item within the header is a small button for toggling the menu. I'm using a background icon to fill in text where screen readers just see the word "menu". This is a better solution than static images just to increase visibility.

CSS Design

Moving into my stylesheet I've included a small collection of resets along with the page structure.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
header {
  display: block;
  width: 100%;
  height: 52px;
  background: #5186a8;
  padding: 15px 10px;
  margin-bottom: 25px;
}
 
#navbar {
  max-width: 1000px;
  margin: 0 auto;
}
 
.menubtn {
  /* needs positioning for z-index http://stackoverflow.com/a/10600930/477958 */
  position: relative; 
  z-index: 101;
  color: #274a61;
  text-decoration: none;
  font-size: 0em;
  line-height: 0em;
  top: 2px;
  padding: 15px;
  background-image: url('hamburger.png');
  background-position: 50% 50%;
  background-size: 25px 25px;
  background-repeat: no-repeat;
}
.menubtn:hover, .openmenu .menubtn {
  color: #bdd43e;
  background-image: url('hamburger-active.png');
}

All of the content is centered using a maximum width of 1000px. Everything can shrink smaller but this max limit keeps the page from expanding too wide and looking stretched. Also notice the .menubtn link needs a z-index property to keep it above the overlay.

When a user clicks to open the menu this overlay div will cover the page. The only 2 ways to hide the menu would be clicking the button again, or clicking somewhere on the lightbox overlay. Using a higher z-index value keeps the link colorful and clearly visible above the page.

For the menu icon I'm using a standard three-bar design from Captain Icon, a free open source webfont. I tried including the actual font files but this created a weird rendering bug in Firefox. So the next best option would be to edit the source and save as PNG background images.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/** toggle menu **/
body.openmenu {
  position: fixed;
  overflow: hidden;
}
 
#pgcontainer {
  padding: 45px 0;
  margin: 0;
}
 
.overlay {
  position: fixed;
  z-index: 99;
  background-color: rgba(0,0,0,0.5);
  bottom: 0;
  right: 0;
  left: 0;
}
 
.openmenu .overlay {
  top: 0;
}

Once the menu opens jQuery will handle a number of tasks, one of which is to append a new class onto the body element. With this class .openmenu the body will stay fixed as you scroll down the navigation. The .overlay class also uses a fixed position to stay on top of the body at all times.

This overlay div uses a position of 0px on all sides of the page. However we don't see the overlay appear until the menu opens and the extra class .openmenu is applied onto the body.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#hamburgermenu {
  height: 100%;
  width: 0;
  background: #373737;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 101;
  overflow: hidden;
  -webkit-box-shadow: 3px 0 7px rgba(0,0,0,0.55);
  -moz-box-shadow: 3px 0 7px rgba(0,0,0,0.55);
  box-shadow: 3px 0 7px rgba(0,0,0,0.55);
}
#hamburgermenu ul {
  margin-top: 45px;
  z-index: 101;
  overflow-y: auto;
  overflow-x: hidden;
}
#hamburgermenu ul li {
  display: block;
}
#hamburgermenu ul li a {
  display: block;
  min-width: 130px;
  padding: 18px 8px;
  color: #cdcdcd;
  font-size: 1.45em;
  font-weight: bold;
  text-decoration: none;
  text-align: center;
}
#hamburgermenu li a:hover {
  color: #fff;
  background: #2c2c2c;
}

Lastly we come to the navigation which is somewhat self-explanatory if you're familiar coding in CSS. There is no set width in CSS so the menu could expand as far as needed using jQuery. The nav links use a minimum width of 130px so the menu doesn't collapse into the text when being hidden from the page.

#hamburgermenu keeps the higher z-index property along with the three-bar menu link. These are the only 2 elements not hidden beneath the page overlay. Also notice how the nav list uses a hidden overflow on the x-axis(side-to-side) with an automatic scrollbar on the y-axis(top-to-bottom). This means if your nav links go beyond the page height you can scroll as if the menu were a separate iframe.

Building the Nav with jQuery

I moved all the JS code into a separate file named hamburger-menu.js. It's not overly confusing but there are ~50 lines of code not worth mixing into the HTML.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$(function(){
  var menuwidth  = 240; // pixel value for sliding menu width
  var menuspeed  = 400; // milliseconds for sliding menu animation time
 
  var $bdy       = $('body');
  var $container = $('#pgcontainer');
  var $burger    = $('#hamburgermenu');
  var negwidth   = "-"+menuwidth+"px";
  var poswidth   = menuwidth+"px";
First we create a number of variables which are used throughout the script. I previously mentioned the menuwidth being set dynamically. Also the animation speed is adjustable by updating the menuspeed variable in milliseconds.
 
The last few variables contain jQuery selector objects to save memory, along with calculations for how many pixels to hide & show the menu.
 
  $('.menubtn').on('click',function(e){
    if($bdy.hasClass('openmenu')) {
      jsAnimateMenu('close');
    } else {
      jsAnimateMenu('open');
    }
  });
 
  $('.overlay').on('click', function(e){
    if($bdy.hasClass('openmenu')) {
      jsAnimateMenu('close');
    }
  });
 
  $('a[href$="#"]').on('click', function(e){
    e.preventDefault();
  });

First take note that I'm referencing a custom function named jsAnimateMenu which takes a parameter of 'open' or 'close'. This is a simple technique to save on excess lines of code. When the .menubtn is clicked jQuery checks the body for an .openmenu class. The if/else logic determines whether the menu is toggled open or closed.

Second I'm checking whenever a user clicks on the .overlay div, which is only possible while it's visible. If the user can click on that div it means the menu is already open and the user wants to close it. My last event handler targets the a[href$="#"] selector.

Basically this prevents any links with the hash symbol from loading into the browser URL, often causing page jumps. Feel free to remove this block when customizing the script for your own website.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  function jsAnimateMenu(tog) {
    if(tog == 'open') {
      $bdy.addClass('openmenu');
 
      $container.animate({marginRight: negwidth, marginLeft: poswidth}, menuspeed);
      $burger.animate({width: poswidth}, menuspeed);
      $('.overlay').animate({left: poswidth}, menuspeed);
    }
 
    if(tog == 'close') {
      $bdy.removeClass('openmenu');
 
      $container.animate({marginRight: "0", marginLeft: "0"}, menuspeed);
      $burger.animate({width: "0"}, menuspeed);
      $('.overlay').animate({left: "0"}, menuspeed);
    }
  }
});

The primary function will use 2 sets of opposing animations based on whether the menu is opening or closing. The container div will move the entire page into negative space off to the right-hand side of the page. This creates a small opening on the left for the navigation menu to slide into place.

Since most of the interface has been customized with CSS properties, jQuery is only responsible for handling the animations. Everything else is managed through CSS properties to ensure positioning and z-index values.

Many designers hold a contention against hamburger menus and try to avoid them in website layouts and mobile apps. And I certainly can't state this is the perfect solution for every website.

But it's entirely reasonable to use this design appropriately and still get a clean user experience. It may be worthwhile to provide a non-JS solution along with the sliding drawer if possible. Overall I'm quite fond of this navigation and I hope my code can provide a template for other curious web designers.