Create a responsive mobile first menu

Part 2 of a series looking at progressively enhanced, mobile first, reponsive navigation

In the first part of this mini series we looked at creating a menu icon for what I believe to be the best solution for your site's mobile navigation.

The outcome was to create an icon by creating an HTML element similar to this -

HTML

<div id="nav-btn"></div>

Which would then be styled with CSS like this -

CSS


#nav-btn { 

background: transparent; 
border-bottom: 23px double #121212; 
border-top: 8px solid #121212;
height: 7px; 
width:37px;
}

Result

Making an example.

Ok, so let's start making a quick example page with some links that we're going to want to manipulate with this button. I could bore you with the HTML and CSS of this but I'm sure you know how to do that kinda stuff, and if not. You can see it from the resulting JSFiddle below -

Querying your users viewport.

Now for this example, I only want the resulting show/hide menu icon to work on small viewports. Not only do I want to only have this working for small viewports (less than 767px for arguements sake) I was also only want it to work on devices such as an iPhone or Android mobile phone. Therefore whereas we would normally use this kind of media query -


@media only screen and (min-width: 240px) {
    /* CSS Rules to go here */
}

We are instead going to define a media query as follows that should only affect viewports found on popular mobile phones, like this -


@media only screen and (min-device-width: 1px) and (max-device-width: 767px) {
    /* CSS Rules to go here */
}

The min-device-width and max-device-width media query rules struggle to work in Android 2.2 so to combat this make sure you've got something similar to this in your HTML

<meta content="width=device-width,initial-scale=1" name="viewport" />

So with this media query we're going to hide the nav element and restyle it.

An updated example (note: this will only look different on your mobile device) -

But first, where's that menu button gone?

In the HTML of the last code example I have not included the DIV for the button. This is because it is going to be added with JavaScript and jQuery. This makes our original HTML cleaner and means your site will be more progressively enhanced and will degrade gracefully when the user has JavaSCript disabled or they're a blackberry user *winks*.

Getting the Javascript going.

So, we're going to add the navigation (menu) button with some JavaScript/jQuery. To do this we're going to use jQuery's prepend, like this -

$('#container').prepend('<div id="nav-btn"></div>');

Well that's all well and good, but.

C'mon, c'mon, c'mon, Touch me.

The menu icon is perfect for touch, not so much for a device that'll recognise the media queries but doesn't have touch. So we're going to throw that bit of jQuery into an if statement in JavaScript to only appear if the device has touch. Like this -


    if ("ontouchstart" in document.documentElement)

    // It's a touch screen device.
    
    // create the button div
      $('#container').prepend('<div id="nav-btn"></div>');
    }

     else 
    {
    }

So to tie-in with our device-width media queries this should now show the menu button when viewed on a touch screen device which's viewport is no bigger than 767px.

More Styling? More JavaScript/jQuery?

I want to style the navigation links differently if the device is touch enabled. As much as I have previously said how I dislike a screen full of links having a menu button that shows and hides the navigation is screaming out for nice big links for our chuby fingers to tap, But to progressively enhance this bad boy we only want it changed on devices that have JavaScript enabled. Therefore we're going to add a class the contents #container. Whilst we're at it let's add the show/hide jQuery code too. Like this -


if ("ontouchstart" in document.documentElement)
    {
    // It's a touch screen device.

    // add claas of touch to container for the CSS 
      $("#container").addClass("touch");
    
    // create the button div
      $('#container').prepend('<div id="nav-btn"></div>');

    // make the button div a toggle show/hide$('#nav-btn').click(function(){
      $('nav').toggle();
      return false;
      })
    }

    else 
    {
    }

Now we add the .touch to the relevant CSS. Here's the updated example.

Having a quick play with this I noticed the dreaded FOUC. So, first I added a 'noJs' class to my HTML then included the following code to swap it with just 'JS' if JavaScript is enabled

<script>
(function(H){H.className=H.className.replace(/bnojsb/,'js')})
(document.documentElement)
</script>

After this I changed the display: none; attirbute for the <nav> element to work if the document has a JS class which gets rid of the FOUC and degrades nicely as well.

You've hidden it? But?

Of course, hiding the <nav> element with display: none; when JavaScript is enabled will hide it regardless of whether the device viewing the site has touch capability or not. Therefore we need to add something to the else statement in the Javascript. That's easily rectifided as shown in this example code - 

 

if ("ontouchstart" in document.documentElement)
    {
    // It's a touch screen device.

    // add claas of touch to container for the CSS 
      $("#container").addClass("touch");
    
    // create the button div
      $('#container').prepend('<div id="nav-btn"></div>');

    // make the button div a toggle show/hide$('#nav-btn').click(function(){
      $('nav').toggle();
      return false;
      })
    }

    else 
    {
     // shown navigation menu
      $('nav').css('display','block');
    }

Here's an updated fiddle including all that fun code -

It's not over until you open it in Opera Mini

Although I've managed to get this working in MobileSafari (iOS 5.1.1), a Android 2.2,2.2.1,2.3.6 and 4.0 browser and Opera Mobile. Opera mini was giving me a headache.

For some reason (to which I guess it's because it is a proxy browser) Opera mini doesn't create the nav-btn div and prepend it to it's #container. Doing this means an Opera mini user won't see the menu AT ALL unless they've got JavaScript disabled.

Having a quick hunt around the internet I found that you can target the browser using JavaScript. So adding this code would then give you the links back, but with out the additional styling also. If you wanted you could then define these again with another class and apply that too. But for now -

       
if (operamini) 
{
 // remove .touch class for opera mini as it can't create the #nav-btn element 
  $("#container").removeClass("touch");
}

else 
{

}

That's all folks!

There we pretty much have it, a menu button that's created with CSS added with JavaScript that if enabled alse changes the styling of your navigation to suit your touch enabled device and your fat fingers.

Here's one last fiddle -

But if you want to go straight to a page just to test it on your device(s) then I've hosted it on my site here too.