WordPress jQuery dropdown menu tutorial

drop-down-final

See a more recent article about how to integrate the Twitter Bootstrap dropdown menu with WordPress here

The motivation for this article was to create a set of notes for myself to speed up the process of creating a WordPress drop down menu with jQuery, as I found myself needing to add this functionality to most projects. I decided to turn it into a tutorial and share it with the world. If you have any issues or if you have a more efficient way of achieving the same end result, I'd love to hear from you.

Step 1: Register the menu in functions.php

Add the code to your functions.php file where 'primary' is the theme_location used in your template file and 'Header Navigation' is the description appearing in the Appearance > Menus section of your WordPress back end.

// WP3 menus
register_nav_menus( array(
	'primary' => __( 'Header Navigation', '' ),
) );

In this case we are only registering one menu but if you wish to add more simply duplicate the line within array () with something like: 'secondary' => __('Sidebar Navigation', ''),

Step 2: Call the menu in your theme.

Add the code below to your header.php file, where:

  • 'container' is the element used for the wrapper. I have used the HTML5 <nav> element.
  • 'container_class' is the CSS class name output to the wrapper
  • 'menu_class' is the CSS class name output to the ul inside the div wrapper 'theme_location' is the name you have assigned in your functions.php file.
<?php wp_nav_menu( array(
	'container' => 'nav', /* default is 'div' */
	'container_class' => 'header-nav',
	'menu_class' => 'sf-menu', 
	'theme_location' => 'primary' 
) )?><!--.header-nav-->

Note: the comment <!--.header-nav--> is to indicate the end of the menu code output by the WordPress function. See a complete list of arguments for this function at the wp_nav_menu function reference.

Step 3. Create the menu in your WordPress back end and assign it to the theme location.

Go to the Appearance - Menus in the WordPress back end to access the "Menus" panel. Click the "+" symbol to create a new menu and give it a name, such as "main". Click the "Save Menu" button to create this menu.

Next, add pages, categories or custom menu links to your new menu and reorder them via drag and drop. You can assign sub-pages simply by dragging a menu item slightly to the right under its parent item. Click Save again to write the changes to the database.

Next, you will see the name you assigned to your menu in your functions.php file in the "Theme Locations" panel. Click the drop-down to assign your new menu to this location and click save. Your menu will now be visible in your theme, however unstyled.

Note: the name given to your menu will be added as a CSS ID to the <ul> tag with prefix "menu-", in addition to a CSS class of "menu". I called the menu "main", so this outputs <ul id="menu-main" class="menu"> immediately after <div class="main-menu">.

Step 4: Add CSS to menu container

nav {	
	float: left;
	clear: left;
	margin-top: 10px; /* adjust as necessary */
}
nav ul.sf-menu {
	padding: 0; margin: 0; /* reset list style */
	background: #CCC; /* give your menu a bg colour, if necessary */
	width: 940px; /* same as header width */
}

Step 5: Include Superfish and jQuery

I have used the Superfish jQuery menu plugin by Joel Birch for the drop down function. Firstly we need to add a reference to the jQuery library in your theme if you have not already done so. My preferred method is the "smart jQuery inclusion method described in this Digging Into WordPress article. This forces your theme to use the Google hosted jquery file. Note: the version number /1/ in the URL string will automatically pull the latest hosted version of jquery.min.js

// smart jquery inclusion
if (!is_admin()) {
	wp_deregister_script('jquery');
	wp_register_script('jquery', ("http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"), false);
	wp_enqueue_script('jquery');
}

Download the superfish.js and hoverIntent.js files and save them to a relevant location within your theme. I have created a directory called 'includes' for files such as this. Now link these files in the head section of your theme, just below <?php wp_head(); ?> using script tags.

<script type="text/javascript" src="<?php bloginfo('stylesheet_directory')?>/includes/hoverIntent.js"></script> 
<script type="text/javascript" src="<?php bloginfo('stylesheet_directory')?>/includes/superfish.js"></script>

...or add load these two JavaScript files via wp_head() by adding the following code to your functions.php file just below the wp_enqueue_script('jquery') line

// load Superfish and HoverIntent via wp_head()
	wp_register_script('hoverIntent', get_bloginfo('template_directory') . '/includes/hoverIntent.js', null, null, false);
	wp_enqueue_script('hoverIntent');
	wp_register_script('superfish', get_bloginfo('template_directory') . '/includes/superfish.js', null, null, false);
	wp_enqueue_script('superfish');
}

Step 6: Add the Superfish 'Essential Styles' CSS to your theme's style.css file

/*** ESSENTIAL STYLES ***/
.sf-menu, .sf-menu * {
	margin: 0;
	padding: 0;
	list-style: none;
}
.sf-menu {
	line-height:	1.0;
}
.sf-menu ul {
	position: absolute;
	top: -999em;
	width: 10em; /* left offset of submenus need to match (see below) */
}
.sf-menu ul li {
	width: 100%;
}
.sf-menu li:hover {
	visibility: inherit; /* fixes IE7 'sticky bug' */
}
.sf-menu li {
	float: left;
	position: relative;
}
.sf-menu a {
	display: block;
	position: relative;
}
.sf-menu li:hover ul,
.sf-menu li.sfHover ul {
	left: 0;
	top: 2.5em; /* match sf-menu line height */
	z-index: 99;
}
ul.sf-menu li:hover li ul,
ul.sf-menu li.sfHover li ul {
	top: -999em;
}
ul.sf-menu li li:hover ul,
ul.sf-menu li li.sfHover ul {
	left: 10em; /* match ul width */
	top: 0;
}
ul.sf-menu li li:hover li ul,
ul.sf-menu li li.sfHover li ul {
	top: -999em;
}
ul.sf-menu li li li:hover ul,
ul.sf-menu li li li.sfHover ul {
	left: 10em; /* match ul width */
	top: 0;
}

Step 7: Add the superfish script to your head element.

See here for a list of the available options for superfish.

<script type="text/javascript"> 
// initialise Superfish 
$(document).ready(function(){ 
  $("ul.sf-menu").superfish({ 
animation: {opacity:'show',height:'show'}, // fade-in and slide-down animation
delay: 500, // 0.5 second delay on mouseout 
speed: 'normal'
  }); 
}); 
</script>

Step 8: Add the 'skin' CSS to your theme's style.css file and amend as desired.

/*** DEMO SKIN ***/
.sf-menu {
	float:			left;
	margin-bottom:	1em;
}
.sf-menu a {
	border-left:	1px solid #fff;
	border-top:		1px solid #CFDEFF;
	padding: 		.75em 1em;
	text-decoration:none;
}
.sf-menu a, .sf-menu a:visited  { /* visited pseudo selector so IE6 applies text colour*/
	color:			#13a;
}
.sf-menu li {
	background:		#BDD2FF;
}
.sf-menu li li {
	background:		#AABDE6;
}
.sf-menu li li li {
	background:		#9AAEDB;
}
.sf-menu li:hover, .sf-menu li.sfHover,
.sf-menu a:focus, .sf-menu a:hover, .sf-menu a:active {
	background:		#CFDEFF;
	outline:		0;
}

The finished product

Here is an exmaple of a drop down menu created with the above steps, and then given some custom style.

Additional note: Cleaning up the WordPress code

I have just read an article by Craig Buckler on Sitepoint called How to Tidy Your WordPress Menu HTML. This is explains how to prevent a lot of the unnecessary HTML output by the wp_nav_menu function. I can't wait to put this into practice. Love your work, Craig!

16 Responses to “WordPress jQuery dropdown menu tutorial”

  1. Jon says:

    Hi, I’ve got this part working on wordpress site. Trouble is it only starts working after the first mouseout? Can’t figure out why? Any ideas? Thanks.

    • astrotim says:

      I would remove the jQuery and see if you get the drop down effect just with the CSS. The CSS alone should handle the show/hide of the submenu items. Also, check your source code and click the links that the WordPress header file outputs to make sure the superfish.js and hoverIntent.js files are referenced correctly. My preferred troubleshooting method for any WordPress code issue is to copy the source code output by a page and save it as a static HTML file on a local web server. Then I start removing as much of the code as possible to try to eliminate the possibility of other conflicts. If it’s still misbehaving, send me a link to your site and I’ll take a look.
      – Tim.

  2. This was really really helpful…thank you 🙂 I already had a superfish menu working but wasn’t sure how to get it to play nice with WordPress 3.0 menus. This really helped Tim. I have a quick question though… do you know of a way to add a graphic separator between the horizontal top menu items using this method? I had it all working previously using a function and wp-list-pages.

    • astrotim says:

      Hi Lisa, I’m glad you found the article useful! As for your graphic separator question, I can suggest two ways of doing this: Firstly, you could add the graphic as an li background style and then override the style for the first list item with {background: none; margin: 0; padding: 0} etc, so every list item except the first has the graphic on the left. This would probably require some tweaking of the superfish CSS so the drop down effect doesn’t appear under the separator graphic. The second way would be to use the ‘custom links’ feature in the WordPress menu manager to add a blank menu item in between each actual menu item, then style all these with your separator graphic as a background image. Hope that helps! Cheers, Tim.

  3. Derrick says:

    Hello … this tutorial was VERY timely for one of my projects. What’s great about your menus is its flexibility. You get all the bells and whistles if javascript is enabled. Even if it is isn’t, it works great and entirely functional.

    Again, outstanding … all of this without use of a plugin AND allows for use of the new menu capabilities in wordpress from the admin screen! Bravo. V/R, Derrick

    Note: Can this be adapted to vertical menus? Thanks again!

  4. JeroenG says:

    Nice! Thanks! I was looking all day for an nice dropdown menu which allow me to use the WordPress 3 menu.

    Still one question. Is it possible to use a different color for the menuitem in the dropdown menu?

    In your example, i want the page-buttons in the color white and the subpage-buttons and hover color to be red.

    Now the page and subpage buttons are the same color..

    • Tim says:

      You’re welcome, Jeroen. If you want to style the dropdown items differently, just use the appropriate CSS selectors to apply different background color. For example .sf-menu li selects all the list items inside the element with class=”sf-menu”, so you’d need to override this selection for submenu items with a more specific selector like .sf-menu ul li which would select only list items that are inside a ul element that is inside the sf-menu class. You might also need to select the link element to get your normal and hover state styling the way you want, in which case it would be .sf-menu ul li a and .sf-menu ul li a:hover

  5. Jeff says:

    This tutorial was very helpful for me. Thank you. I have superfish working now with one little problem that I cannot figure out:

    When I mouseout, I don’t get the forgiveness delay. Any ideas on what might be the cause?

    Thank you.

    • Tim says:

      Hi Jeff,

      The effects are handled by jQuery, specifically the $(“ul.sf-menu”).superfish action, inside which is the ‘delay’ parameter with a value of mouseout delay time in milliseconds. Firstly, I would check to see whether the jQuery library is correctly included in your document – sounds obvious, but I’ve been caught out by that one before! Look at your source code and click the link to the jQuery library to see whether it appears. Then do the same for the superfish.js and hoverIntent.js files.

      Next, check whether your selector is targeting the menu correctly – eg: $(“ul.sf-menu”). You can try something like $(“ul.sf-menu”).css(“font-weight”,”bold”) which will make all the text appear bold if your selector is targeting the right element. Then try adding the superfish action back in with the delay: 500 parameter to see if that works.

      Another method I commonly use for troubleshooting is to view the source code from a WordPress generated page and save it as a static HTML file. I then start removing all the parts of the page which don’t relate to my problem in order to try to isolate what code I am trying to debug and then use a process of trial and error on a much simplified page.

      I hope that helps

      Tim.

  6. Jeroen Gerth says:

    Thanks! It works! Now i have the navigation exactly like i want.

    Still one question. How can i highlight the current active page? The selector .sf-menu a:active doesn’t seem to work.

    • Tim says:

      Good to hear Jeroen. To style the list item for the active page, you need to add a style rule for class=”current_page_item”. WordPress adds this class dynamically, so if you have a look at your source code with Firebug (or Chrome’s inspect element), you’ll see it in action.

      The :active pseudo selector is for the state of a link when it is being clicked; as in before the mouse button is released. It is often styled the same as the :hover pseudo state, however a typical use-case is for a button style, where the :active state is styled to make the button move down 1px to give the illusion of pressing down on the button.

  7. Ant says:

    Hi Tim,

    Very interesting article and have steered clear of JQuery in WordPress so far, just curious as fairly new to WordPress and familiar with editing the menu system with CSS/CSS3 but new to adding JQuery.

    WordPress already looks at Google for the latest version of JQuery as I understand it, so if I didn’t use Superfish I would I just need to add the relevant script into the head as per step 7 in your tutorial?

    Cheers,

    Ant

    • Tim says:

      Hi Ant,

      Apologies for the late reply. By default, WordPress loads jQuery locally from the core /wp-includes/js/ directory, not Google. The deregister/register code to which I refer in that article is simply a way of ‘unhooking’ the local version and ‘hooking’ the Google CDN version. This is commonly considered a method of speeding up the load time of a site for two main reasons: the user may already have a copy of the Google CDN jQuery file cached in their browser, and if not, a browser can load JavaScript files from different domains simultaneously which speeds things up. Having said all that, it’s only milliseconds we’re talking about, so unless speed is really really crucial, then it’s just the nerd factor (which I admit appeals to me). Loading jQuery from the WordPress core is totally fine and at the end of the day, Superfish doesn’t care where jQuery came from, as long as it’s there.

      The one important thing you need, however, is to load the Superfish and HoverIntent jQuery plugins as the script in step 7 is calling the superfish() method defined within the superfish.js file. I have amended step 5 to show how to load these files via wp_head() using wp_enqueue_script in your functions.php file.

      I should also note that I wrote this article nearly two years ago, so there’s a lot of other options for drop down menus. I have a drop down demo page here which is CSS-only (no jQuery animation), and I also have recently starting using the Twitter Bootstrap drop down plugin, which is pretty slick.

      Hope that helps!

      Tim.

  8. Abbe says:

    Is it possible to make it show drop down after click rather than hover?

    • Tim says:

      The drop down display on click is actually the default behaviour of Bootstrap. So if you skip the last point I make in “step one”, this should work the way you want.

  9. ali says:

    Wow! Such a step by step manual. It is very useful for me as a jquery beginner. Thanks a lot.