Customizable jQuery Drop-Down Menus

Drop-down menus are standard in websites to easily organize site navigation. By applying a few lines of jQuery and accompanied with some CSS, you can easily create some slick looking menus. A while back I wrote about “Generating a Dynamic Selection DropDown with jQuery and AJAX.” For simplicity of that tutorial, I didn’t focus on drop-down menus, but you could easily apply the dynamic content driven in that tutorial to the methods explained here.

View the demo to see the functioning drop-down in action to get an idea what I will be explaining.

Getting Started

First off, you will need your content marked-up using good semantic standards. It is best practice to use unordered lists for navigation mark-up. I use “.nav” class for the base navigation class name and “.subnav” for any subsequent-level navigation. I sometimes use an ID rather than a class for the base navigation, but if you wish to reuse the drop-down jQuery for multiple navigation bars on one page, then keeping them more general is beneficial for code reuse.

The CSS

You can make the lists look however you please within the CSS; for simplicity, the design in my tutorial has basic styles applied to it. Positioning is really the key to making your drop-down not only look right, but also function properly as well. My styles use direct selectors in my example, but you may need to use a workaround if you need it to work in older browsers (such as IE). All of the subnav items have an absolute position to pull them out of the regular flow of the document. Every subnav after the first has an additional position offset (left:5em) to push it over from its parent element. It also has a negative margin so that the first item will base-align with its parent. I have also set up “activeList” classes that get applied to the list through the jQuery when it has been moused-over (hover), and then removed upon mouse-out.

Building the jQuery

Applying the jQuery to the lists to make the drop-downs functional is easier than you might think because of how powerful jQuery is. One of the most important parts about jQuery is understanding how to leverage the power of selector use. Any general selector look-up will apply to all elements found within that selector tree. For example $(".nav li") will search find every and all “li” elements within the “.nav” class. By using the children() method, jQuery will only select the direct descendants of the parent that will be shown later.

$(".nav li").hover(function() { // selects every "li" element found within ".nav"
     // hover state
}, function(){
     // out state
})

One important part of this jQuery is that it uses setTimeout() to not immediately trigger the slideDown() of the menu so that if the user accidentally rolls away from the menu prematurely, it will not disappear. So, the first thing is to set up a timeout variable and then bind it to the element being hovered over by using the jQuery data method. If timeout exists when hovered over, then the timeout is reset and the subnav will not reset:

...
          var timeout = $(this).data("timeout");
          if(timeout) clearTimeout(timeout);

After the timeout is set the animation in can be set. This can be whatever method you find suitable for your design. I have used slideDown in my example, but there are many ways to bring in your subnavs such as fadeIn(), show(), animate(), to name a few. Once the animation is complete you can trigger an event handler. I add a class to the parent “li” to give it separate styling to indicate it is active.

$(this).children("ul.subnav").slideDown(350, function(){
               // onComplete
               $(this).parent().addClass('activeList')
          })

On the out state of the hover (mouseOut), the timeout data bound to the button is triggered through a proxy. Without a proxy, context of the timeout points to a different object reference and cannot clear the original timeout object. If the timeout isn’t cleared, then its associated event handler will still trigger whether or not the user’s cursor slips off and on the intended button. In other words when you roll in and out of a button quickly the timeout never gets reset and the out state event handler may trigger prematurely. So on mouseOut you will see:

...
          // out state
          $(this).data("timeout", setTimeout($.proxy(function() {
                 // animate out
          }, this), 300)); // timeout set for 300 milliseconds

Once the proxy triggers its event handler, the animation out will occur. Again, the animation here can be set to the desired design for the site. I have used slideUp(), but you can play with other ways to animate it out. Once the animation is complete, you can trigger another handler to remove the activeList class that was placed there previously.

$(this).children("ul.subnav").slideUp(200, function(){
  // onComplete
  $(this).parent().removeClass('activeList')
})

Here is the code in it’s entirety that I keep in a separate .js file:

// JavaScript Document
function setDropDown(){
	$(".nav li").hover(function() {
		var timeout = $(this).data("timeout");
		if(timeout) clearTimeout(timeout);
		$(this).children("ul.subnav").slideDown(350, function(){
			// onComplete
			$(this).parent().addClass('activeList')
		})
	}, function() {
		// out state
		$(this).data("timeout", setTimeout($.proxy(function() {
			$(this).children("ul.subnav").slideUp(200, function(){
				// onComplete
				$(this).parent().removeClass('activeList')
			})
		}, this), 300)); //timeout set for 300 milliseconds
	});
}

Endless Possibilities

There you go. In just a few lines of code, you can easily implement a drop-down menu that will work with an infinite number of subnav elements you have nested within your navigation. Now it is your turn to give it a try. There are endless design possibilities once you figure the basics of implementing the code. Hopefully I have broken it down enough to make more sense of it. If you have any questions, please feel free to leave a comment.