How to create mobile style slide-in navigation

In this tutorial we’ll look at the techniques needed to make a navigation menu that is hidden off screen until the user clicks on a menu icon, at which point the content slides over and darkens, and the menu slides in. The menu will also be responsive vertically, filling the height of the browser window on whatever size screen it’s being looked at in.

To achieve this we will be using a couple different methods, one of which being flexbox, which is turning into a real ‘buzzword’ at the moment for being the holy grail of web layout methods. We will be using it to make the menu fit the height of the window. We’ll also be using jQuery for the actual functionality of the menu, making it slide out on a click event, and also providing a fallback for if the user doesn’t have JavaScript enabled in their browser (which we’ll detect with Modernizr).

Here’s what it’ll look like when finished. And if you’d like to follow along with the full code you can download it here.

 

Getting started with the markup

Let’s first of all look at out index.html file that will be used in our demo, I’ll show you everything that’s in there and then we can run through piece by piece and look at the important parts:

<!doctype html>
<html class="no-js">
<head>    
    <meta charset="utf-8">
    <title>Full-height Off Screen Menu</title>
    <link href='http://fonts.googleapis.com/css?family=Montserrat:400,700|Open+Sans:400italic,400,700' rel='stylesheet' type='text/css'>
    <link rel="stylesheet" href="css/styles.css">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="js/modernizr.js"></script>
    <!--[if lt IE 9]>
    <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
    <![endif]-->
</head>
<body>
    <header class="cf">
        <h1>Full Height Off Screen Menu</h1>
        <a href="#" id="navicon" class="closed">&#9776;</a>
        <ul id="fallback-nav">
            <li><a href="#" class="current">Home</a></li>
            <li><a href="#">About</a></li>
            <li><a href="#">Work</a></li>
            <li><a href="#">Blog</a></li>
            <li><a href="#">Contact</a></li>
        </ul>
    </header>
    <nav id="main-nav">
        <a href="#" class="current"><div>Home</div></a>
        <a href="#"><div>About</div></a>
        <a href="#"><div>Work</div></a>
        <a href="#"><div>Blog</div></a>
        <a href="#"><div>Contact</div></a>
    </nav>
    <div id="main-content">
        <article>
            <h2><a href="#">Article Title</a></h2>
            <span class="date">Published 25th February 2014</span>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur sodales ligula in libero. Sed dignissim lacinia nunc. <a href="#">Read more &rarr;</a></p>
        </article>
        <article>
            <h2><a href="#">Etc.</a></h2>
            <span class="date">Published 25th February 2014</span>
            <p>... <a href="#">Read more &rarr;</a></p>
        </article>
        <p><a href="#" class="older">&lt; Older Posts</a></p>
    </div>
    <div id="fade"></div>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script type="text/javascript" src="js/scripts.js"></script>
</body>
</html>

Let’s dig in! The only thing to note in the <head> tag is that we’re calling in some Google Fonts. We’re also linking to our css file as well as a modernizr.js file which you can download from here which we’ll use to detect if the user’s browser has JavaScript enabled or not, so that we can provide a fallback (that’s why the html tag has a class of ‘no-js’ to begin with, to make a default page that will appear if there’s no JavaScript, if it is enabled, Modernizr will swap that class name for us).

Next the only other real things of interest our the fallback navigation which is the <ul> with an id of ‘fallback-nav’ in the <header> as well as the link with an id of “navicon” that will be the main link used to create our effect. And then the <nav> with an id of “main-nav” which will be (as you can guess) our main navigation used in the effect. The only other element used in the effect is that last <div> with an ID of “fade” which will be our black cover that darkens the content when the menu icon is clicked.

Finally we call in jQuery from Google and our own script file which is where we’ll be working in a bit. But first let’s go through the styles.

 

Styling with CSS

So we won’t be going through all of the CSS styles (if you want you can inspect or ‘view source’ on the demo) as they are in the most part used for the general styling of the page, which is not the goal of this article. We’ll be breaking it up and looking at a few chunks of code which are important to the effect. So, first of all:

html.no-js #fallback-nav { display: block; }
html.no-js .fade { display: none; }
html.no-js #navicon { display: none; }
html.js #fallback-nav { display: none; }

We’ll kick things of by making sure that if the user’s browser does not have JavaScript available (that’s that class name on the HTML element that we saw earlier using Modernizr) then we’ll show the #fallback-nav in the header, and we’ll hide the #fade DIV as well as the navigation icon. This is our fallback solution so the fallback menu needs to be styled in a more traditional manner. We’re also hiding this solution if JavaScript is available, as you can see in the last line.

#navicon {
    float: right;
    font-size: 2em;
    text-decoration: none;
    position: relative;
    z-index: 9; }

#navicon.open { color: white; }
#navicon.open:hover { color: #e6e6e6; }
#fade {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    background: #000;
    opacity: 0.5; }

So in this section we’re styling our menu icon in the header, it’s pretty simple, just giving it a relative position and a high z-index (which will be useful later when the rest of the content gets faded out it will stay on top). We’re also changing the color to white when it’s got a class of “open” which we’ll be adding and taking away using jQuery. We can also see that the single div with an id of “fade” stretches to fill the entire screen and filled with a solid black with a 50% transparency. If the user has no JavaScript then this black filter will be hidden, if not we’ll hide it with jQuery. Now let’s look at the menu itself:

#main-nav {
    position: fixed;
    height: 100%;
    top: 0;
    right: -250px;
    background: #222;
    max-width: 250px;
    width: 100%;
    z-index: 5;
    text-align: center;
    display: flex;
    flex-direction: column; }

#main-nav a {
    flex: 1;
    color: white;
    border-top: 1px solid #555;
    text-decoration: none;
    display: flex;
    flex-direction: column;
    justify-content: center; }
#main-nav a:hover, #main-nav a.current { background: #3c3c3c; }

So last but not least the main menu. I must add first of all that this is the simplified code that would need quite a few vendor prefixes before being cross-browser compatible. To achieve this I recommend using an awesome tool like Autoprefixer to add all the correct prefixes for you.

So that being said, let’s look at what it entails: first of all it’s got fixed positioning at the top of that page and negative 250px to the right. This means that it’s there but just “off-screen” (as it has a max-width of 250px). The height is also 100%, so that it fills the whole window from top to bottom. The menu also needs a z-index between 0 and 9 (above the black filter). Then, here is when the magic happens, it has a display: flex; property, as well another linked property of ‘flex-direction’ (which we’ve set to “column” here instead of it’s default “row” which means the flex item children—the links in the menu—will fill the entire height of its parent in equal parts).

So that’s all been targeting the menu holder #main-nav, next when styling the links we give them a flex value of 1. This is what makes the links fill the whole height equally. Then these links are also given a display value of ‘flex’ themselves, which means that any elements inside of the links will be affected by this. We make sure the flex-direction is still ‘column’ and then we add a property of “justify-content: center;”. This makes the text itself inside of the links centred vertically as well (this is why there are divs inside of the <a> tags of the #main-nav, which is not exactly semantic but is a very quick and easy way to vertically center items.)

Now we can’t see anything we’ve just done quite yet, we now need to add our functionality with our own scripts.js file that we called earlier.

 

Adding the functionality with jQuery

The script for this effect is very small and simple, but I’ll put it all here first and then explain what’s happening:

$(document).ready(function() {
    $('#fade').hide();
    $('#navicon').click(function() {
        if($('#navicon').hasClass('closed')) {
            $('body').animate({left: "-250px"}, 400).css({"overflow":"hidden"});
            $('#main-nav').animate({right: "0"}, 400);
            $(this).removeClass('closed').addClass('open').html('x');
            $('#fade').fadeIn(); }
        else if($('#navicon').hasClass('open')) {
            $('body').animate({left: "0"}, 400).css({"overflow":"scroll"});
            $('#main-nav').animate({right: "-250px"}, 400);
            $(this).removeClass('open').addClass('closed').html('&#9776;');
            $('#fade').fadeOut(); }
    });
});

So first of all we hide the black filter. Then next all we do will be contained inside a function attached to a click event that is bound to the menu icon link. When that is clicked we have two different cases or situations; one is when the menu is already hidden (like the default state) or one when the menu is showing. So the menu icon link has a class name of closed that we added, and we also styled for open. Our first “if” statement is that if the link is closed (therefore default). If that’s the case, then the whole <body> element is animated 250px to the left, and stops the scrolling of the page. The #main-nav div is also being animated into place, we’re changing it’s right value from -250px to 0. Then we’re removing the class of ‘closed’ from the link and adding one of ‘open’ as well as changing the html from the special character of three lines to an “x”. Finally we’re fading in our black filter to make the rest of the content dark. And that’s it! That gives us the open state of the menu.

Now the “else if” statement is targeting the open class on the menu link. When it does we do the reverse of everything we did before, moving the body back into position, as well as moving the menu off-screen again. Removing the class of ‘open’ from the menu icon, adding ‘closed’ and changing the content back to the icon as well as fading out our #fade div. If everything goes according to plan this is what the functionality should look like:

offscreenmenu

 

Conclusion

So there we have it! That’s one way to make this cool effect which we’re seeing more and more now with the style of modern webdesign, especially on mobile designs. This effect is a nice and simple solution that doesn’t require any plugins and can be fully customised to the needs of each project. Also it’s been a chance to make use of flexbox as well as a few other useful tools. Let me know in the comments if there’s anything you would’ve done differently or just what you thought!

Featured image/thumbnail, sliding door image via Shutterstock.

0 shares
  • http://funsurf-blog.blogspot.com/ Satyajit Sahoo

    jQuery animate isn’t probably the best thing here. css transform will make it much smoother and GPU accelerated.

    The markup could be something like this (of course, needs vendor prefixes, and the fadein can be css animation too, just needs a longer syntax, so didn’t write here),

    $(document).ready(function() {

    $(‘#fade’).hide();

    $(‘#navicon’).click(function() {

    if($(‘#navicon’).hasClass(‘closed’)) {

    $(‘body’, ‘#main-nav’).css({

    “overflow”: “hidden”,

    “transition”: “400ms ease”,

    “transform”: “translate3d(-250px, 0, 0))”

    });

    $(this).removeClass(‘closed’).addClass(‘open’).html(‘x’);

    $(‘#fade’).fadeIn(); }

    else if($(‘#navicon’).hasClass(‘open’)) {

    $(‘body’, ‘#main-nav’).css({

    “overflow”: “scroll”,

    “transition”: “400ms ease”,

    “transform”: “translate3d(250px, 0, 0))”

    });

    $(this).removeClass(‘open’).addClass(‘closed’).html(‘☰’);

    $(‘#fade’).fadeOut(); }

    });

    });

    I will prefer the CSS to be in a separate stylesheet file as it’ll be little longer and make the JS just add and remove classes. But it’s totally worth it for the gain in performance.

    • Ivanov Karmazov

      Would this option even work on older browsers? Just curious.

  • Adam

    I know this isn’t the primary focus of the article but I’m a bit curious why you chose to use jquery’s animate function as opposed to CSS3 given that it’s more widely supported than flex layout?

    I’d also like to point out that styling by ID selectors is bad practise as it doesn’t create reusable code, a stumbling block for many a budding front end developers who might look to a tutorial for advise.

    • http://www.justmuziq.com/ Lion

      I was thinking the same thing myself. I think it would be much cleaner using css transitions.

  • Drew McDowell

    Flexbox isn’t supported before IE10, so how does it deprecate?

    • http://www.keytoe.nl David Jon

      I believe it just simply reverts back to a block element. Perhaps you could use something like modernizr to add flexbox to browsers that support it and otherwise just build it up with blocks an perhaps some simple javascript.

      On another note. Personally I would like to burn IE to the ground :) They’ve been holding back frontend development for far too long. Though I’m glad to see they’re finally realizing they have to step up their game.

  • http://pixelhint.com ijja

    is there any difference between what you did and using the .toggleClass() function, it’s more easier i think.

    $(“#navicon”).click(function(){

    $(“#navicon”).toggleClass(“open”);

    …etc

    });