Quantcast
Channel: EricJHansel.com » Eric Hansel
Viewing all articles
Browse latest Browse all 10

A Responsive Drop Down Navigation Menu Part 2

$
0
0

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.


Viewing all articles
Browse latest Browse all 10

Trending Articles