A Responsive Drop Down Navigation Menu Part 2

This is the second post in a series about responsive drop down menus. Start here if you haven’t read the first post in this series.

Responsive Drop Down Menu on iPad and iPhone.

Responsive Drop Down Menu on iPad and iPhone.

Download the files here. Check out the demo here (demo has fake touch added).

In the first post I built out the html and CSS for a responsive drop down navigation menu. In this post I’m going to add some javascript that will improve the usability of the menu on most touch devices and load in superfish.js on desktop browsers.

The problem with responsive drop down menus that use the hover event

One big problem with the responsive menu I created in the first post of this series is that it uses the hover event to drop the menus down. On mobile and touch screen devices the click and the hover events are often registered at the same time. So if the top-level list items of your drop down menu have links they will always register and send the user to that link before the drop down menus drop.

There are a few different ways that you could solve the hover “problem” like using javascript to stop the link event in list items with drop downs, or change your menu to a select element. I wasn’t crazy about either one of those solutions so I came up with my own that adds a drop down indicator that is used to drop the menus down.

Set up the responsive menus for touch devices

If you’re following along and have downloaded the files from the first post all the javascript files you need are already included (You may want to check for newer versions of these files).

The first thing I’m going to do is load in modernizr.js. and test for touch events. “Modernizr is a JavaScript library that detects HTML5 and CSS3 features in the user’s browser.” You can learn more about Modernizr here, but to sum it up quickly Modernizr will test for browser features and add classes to the html tag so you can code accordingly for features detected or missing. If you do a custom build of Modernizr make sure to check the box next to Touch Events. There are other ways to test for touch events and if you can’t or don’t want to use Modernizr you could test for touch events with plain javascript.

NOTE: Not all touch devices register touch events and this solution will not work on those devices. Learn more here.

Secondly, I’m loading in jQuery and making a jQuery plugin called pufferfish. This could probably have been set up without making a plugin, but if someone would like to take it further and add options to it, it’s setup and ready to go.

NOTE: I’m not going to cover making a jQuery plugin, if you’re interested in learning how to make a plugin there are plenty of articles online that cover it.

Testing for touch with Modernizr

The important part here is testing if the html tag has a class of touch. If there’s no touch class I’m going to activate superfish.js.

jQuery makes this easy.

var htmlClass = $('html');
if($(htmlClass).hasClass('touch')){
    //do stuff
}else{
    //do other stuff here
};

NOTE: You could use yepnope.js in combination with modernizr.js to test if a device has reported touch and load in a separate javascript file or call a different function for each. I choose not to use yepnope in this example, but it could easily be adapted.

Next we’ll use jQuery to check if a list item has a drop down (or ul) and if it does we’ll add a link to it that we can use to indicate and activate a drop down.

var obj = $(this);
var items = $('li', obj);
 $(items).each(function(){
     var hasUl = $(this).has('ul');
    $(hasUl).prepend('<a class="menuDrop" href="#"><span>drop down</span></a>').closest('li').addClass('hasDrop');  
};

Then we need add click events to the drop down indicators that we just added. The click event needs to close any open drop downs and open the drop down closest to the indicator.

 $(hasUl).each(function(){ 
    $('.menuDrop',this).on('click', function(){
        var menuToAnimate = $(this).parent('li');
        if( !$(menuToAnimate).hasClass('sfHover') ) {
            $(menuToAnimate).addClass('sfHover');
            $(this).siblings('ul').css('display','block');
            $(this).parent().siblings('li').removeClass('sfHover');
            $(this).parent().siblings('li').find('li').removeClass('sfHover');
            $(this).parent().siblings('li').find('ul').css('display','none');
        }else{
            $(menuToAnimate).removeClass('sfHover');
            $(this).siblings('ul').css('display','none');
            $(this).siblings('ul').find('li').removeClass('sfHover');
            $(this).siblings('ul').find('ul').css('display','none');
        };
        return false;
    });
});

Here’s the final plugin.

(function($){
    $.fn.extend({        
        pufferfish: function() {   
            return this.each(function() {
                var obj = $(this);
                var htmlClass = $('html');
                if($(htmlClass).hasClass('touch')){
                    var items = $('li', obj);
                    $(obj).addClass('pufferfishEnabled');
                    $(items).each(function(){
                        var hasUl = $(this).has('ul');
                        $('ul', this).css('display','none');
                        $(hasUl).prepend('<a class="menuDrop" href="#"><span>drop down</span></a>').closest('li').addClass('hasDrop');        
                        $(hasUl).each(function(){ 
                            $('.menuDrop',this).on('click', function(){
                                var menuToAnimate = $(this).parent('li');
                                if( !$(menuToAnimate).hasClass('sfHover') ) {
                                    $(menuToAnimate).addClass('sfHover');
                                    $(this).siblings('ul').css('display','block');
                                    $(this).parent().siblings('li').removeClass('sfHover');
                                    $(this).parent().siblings('li').find('li').removeClass('sfHover');
                                    $(this).parent().siblings('li').find('ul').css('display','none');
                                }else{
                                    $(menuToAnimate).removeClass('sfHover');
                                    $(this).siblings('ul').css('display','none');
                                    $(this).siblings('ul').find('li').removeClass('sfHover');
                                    $(this).siblings('ul').find('ul').css('display','none');
                                };
                                return false;
                            });
                        });
                    }); 
                }else{
                    $(obj).superfish();
                }
            });
        }
    });
})(jQuery);

Activate the plugin. $(‘ul.main-menu’).pufferfish();

How Pufferfish can be improved

Pufferfish could be improved by adding ARIA to make it more accessible for screen readers. Should be pretty easy to do with what’s already there.
Could add an attribute for aria hidden to: $(‘ul’, this).css(‘display’,’none’);

This version of the menu needs to be tested in IE. I haven’t tested it at all, although I am using almost the same menu on a fairly large website and have tested it back to IE7.

Someone could probably take this concept and write much cleaner Javascript, I don’t claim to be a javascript expert I just like having fun with it.

If you have any questions please leave them in the comments below.

9 Comments

  1. Karl
    Posted August 22, 2012 at 5:26 am | Permalink

    Can it have more than 4 columns? I can’t seem to find in the CSS where this is limited.

    • supa
      Posted September 1, 2012 at 7:27 am | Permalink

      You could have more columns by adding another tag to the ul that structures the nav.

  2. Posted September 24, 2012 at 9:44 am | Permalink

    Hi. I tried to add more Menu in the main menu.But they are not in the row. Why is that? I tryed to modify the css but nothing. Can you help me ? I`m a begginer at css :(

  3. Posted October 4, 2012 at 5:18 am | Permalink

    Hi,

    I love this, has proved very informative.

    The only prob I can’t solve is that when on a pc, the main menu items click and link through to category pages. But on a small screen when the menu button appears, you cannot click the main menu items to access the categories as clicking on them simply opens and closes the sub menu.

    This seems a major problem to me as major category pages cannot be accessed on the mobile device.

    Just thought I would mention it,

    Paul.

    PS Thank you for sharing this, it has given me lots of ideas to work on.

    • Posted October 4, 2012 at 10:35 am | Permalink

      Thanks Paul, I’m glad you like it. I mentioned in the post that I hadn’t tested it in IE at all. Although I do have a similar version of this menu working on another website that has been tested and works great. If you find a solution to the problem I’d love it if you posted it here.

  4. Simone
    Posted November 9, 2012 at 4:50 am | Permalink

    I wanted to include your navbar into my wordpress site. What code do I need to write into the header.php and the function.php in order for it to work in my wordpress site?

    • Posted November 15, 2012 at 8:46 pm | Permalink

      Hi Simone,
      Adding this navigation to a wordpress theme can easily be done, but it’s a bit much for me to answer in a comment. For starters I would try to adapt the css in this tutorial to be used with your theme. Then add the javascript.

  5. mindi
    Posted January 16, 2013 at 4:23 pm | Permalink

    Very well written tutorial on Responsive Menus. I think this is one of the best solutions present to date. Thanks for sharing!

  6. Posted July 10, 2013 at 11:22 am | Permalink

    I love this solution. I have found few others that support both drop down menus and touch support for the “parent” links. Nice job.

    I see in the source code that you have a link to “skip to content” that appears not to work on the demo. Have you enhanced your solution with a link at the top that hides or displays the drop down menu for narrow displays? This would obviate the need for the “skip to content” link.

    Thanks for your work on this.

Post a Comment

Your email is never published nor shared. Required fields are marked *

You may use these HTML tags and attributes <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*
*