Tutorial: Multilevel Menu with CSS and JavaScript

by Mike Nichols

The code for the Multilevel Menu with CSS and JavaScript has been completely revised October 4-17, 2009. This tutorial has been changed to reflect the revisions, as well as being improved with new sections on how to build the menu and troubleshooting.

The Multilevel Menu with CSS and JavaScript is a fast, easy to implement dropdown menu with multiple levels. See it in action at my Thesis Demo site.

The menu files listed here produce a dropdown menu with multiple levels. Shown here is the menu as configured in this tutorial:

Figure 1: Dropdown menu with one level

Figure 1: Dropdown menu with one level

Figure 2: Dropdown menu with three levels

Figure 2: Dropdown menu with three levels

Included in the zip file provided for the menu is a configuration that mimics the standard Thesis navigation bar:

Figure 3: Dropdown menu configured to look like Thesis

Figure 3: Dropdown menu configured to look like Thesis

The Multilevel Menu offers these outstanding features:

  • Easy to set up and use.
  • Requires no plugin. Everything is done with CSS and JavaScript.
  • Any kind of URL, document or file can be put in the menu — no restrictions and no conditional php!
  • Dropdown menus with as many tabs and levels as you want.
  • Tabs may be in any order you want.
  • Dropdown menus have a sliding effect that is very appealing
  • Completely configurable as to size of tabs, font characteristics, colors, etc. with CSS.
  • Includes support for most major browsers, including most versions of Internet Explorer.
  • SEO friendly: Its unordered list is generated within the HTML of the page, so Google’s and other bots can read and index it.
  • Very fast execution. Uses Google libraries for speed.
  • Files include both a colored menu and one set up to look like the standard Thesis navigation menu.
  • In browsers with JavaScript disabled, the main tabs are still usable (but not the dropdowns)

The Multilevel Menu is based on an idea from the Dynamic Drive’s CSS Library that I modified for use with Thesis.

There is one drawback to the Multilevel Menu system: It requires working with the custom_functions.php file, even though the coding is minimal and very easy to do. If you would prefer using a plugin that has a gui interface for configuring a dropdown menu, there is a good tutorial, “Thesis Theme Tutorial – adding multilevel, nested, SEO friendly navigation menu to WordPress” for using the WordPress navigation list plugin, NAVT.

Rather than copy the files shown below, I recommend you get everything — all files and the arrow graphics — in a single zip file. You can download it by clicking here.

Navigation in the Tutorial

This description of the Multilevel Menu with CSS and JavaScript is in five parts. If you want to skip to a particular part, just click on the links below:

How the menu works

There are three separate groups of code and two images that make this menu work:

  • A JavaScript file that does the actual work of building the menu
  • A CSS file that styles the menu
  • A set of functions to put into your custom_functions.php file
  • Two arrow images

These are listed and described below to help you understand how the menu works. All this code and the arrows are included in the zip file.

The JavaScript that builds the dropdowns

The following JavaScript does the actual work of building the multiple levels of the menu. I made some minor modifications to it, but it’s basically a script provided by Dynamic Drive. If you choose to create a file from the code below rather than use the zip file, you must name the file “jqueryslidemenu.js”.

/*********************
//* jQuery Multi Level CSS Menu #2- By Dynamic Drive: http://www.dynamicdrive.com/
//* Last update: Nov 7th, 08': Limit # of queued animations to minmize animation stuttering
//* Modified by Mike Nichols - July 10, 2009 - October 17, 2009
//* Menu avaiable at DD CSS Library: http://www.dynamicdrive.com/style/
*********************/

var jqueryslidemenu={

//Change the duration of the slide in/out animation - in milliseconds
animateduration: {over: 200, out: 100}, 

buildmenu:function(menuid, arrowsvar){
	jQuery(document).ready(function($){
		var $mainmenu=$("#"+menuid+">ul")
		var $headers=$mainmenu.find("ul").parent()
		$headers.each(function(i){
			var $curobj=$(this)
			var $subul=$(this).find('ul:eq(0)')
			this._dimensions={w:this.offsetWidth, h:this.offsetHeight, subulw:$subul.outerWidth(), subulh:$subul.outerHeight()}
			this.istopheader=$curobj.parents("ul").length==1? true : false
			$subul.css({top:this.istopheader? this._dimensions.h+"px" : 0})
			$curobj.children("a:eq(0)").css(this.istopheader? {paddingRight: arrowsvar.down[2]} : {}).append(
				''
			)
			$curobj.hover(
				function(e){
					var $targetul=$(this).children("ul:eq(0)")
					this._offsets={left:$(this).offset().left, top:$(this).offset().top}
					var menuleft=this.istopheader? 0 : this._dimensions.w
					menuleft=(this._offsets.left+menuleft+this._dimensions.subulw>$(window).width())? (this.istopheader? -this._dimensions.subulw+this._dimensions.w : -this._dimensions.w) : menuleft
					if ($targetul.queue().length<=1) //if 1 or less queued animations
						$targetul.css({left:menuleft+"px", width:this._dimensions.subulw+'px'}).slideDown(jqueryslidemenu.animateduration.over)
				},
				function(e){
					var $targetul=$(this).children("ul:eq(0)")
					$targetul.slideUp(jqueryslidemenu.animateduration.out)
				}
			) //end hover
		}) //end $headers.each()
		$mainmenu.find("ul").css({display:'none', visibility:'visible'})
	}) //end document.ready
}
}

//build menu with ID="slidemenu_jq" on page:
jqueryslidemenu.buildmenu("slidemenu_jq", arrowimages)

The CSS that styles the menu

Following is the CSS file that styles the menu. You have complete control over the fonts, the menu colors and borders, the tab size and spacing, and the dropdown items' size, among other things. The values in the CSS are those that I used to style the green menu on the Thesis Demo site, but you are completely free to change anything you wish.

I also made a version of this CSS code that mimics the standard Thesis navigation bar. The code for this version is not listed here, but it is in the zip file.

If you choose to create a file from the code below rather than use the zip file, you must name the file "jqueryslidemenu.css".

/* STYLING FOR THE MULTILEVEL DROPDOWN MENU WITH CSS AND JAVASCRIPT
Produces the green menu as seen on Thesis Demo
Modified and annotated by Mike Nichols - July 10, 2009 
Revised October 4, 2009 */

/* COLOR CHART
#408000	- fern green	- menu strip
						- tabs
#53A502	- medium green	- tab dividers
#006500	- dark green	- submenu item dividers
#007200	- very dark green - tab and submenu item hover state
#80FF00	- lime green	- font hover state
*/

/* Class for Menu */
.slidemenu_class {
	/* font styling */
	font-weight: bold;
	text-transform: uppercase;
	font-size: 1.15em;
	font-family: Verdana, Arial, sans-serif;
	/* menu strip color */
	background: #408000;
	/* menu strip width */
	width: 100%;
}

/* Top level menu tabs */
.slidemenu_class ul li a{
	/* background color of tabs (default state) */
	background: #408000;
	/* font color (default state) */
	color: #FFFFFF;
	/* tab and menu strip height above letters */
	padding-top: .5em;
	/* tab and menu strip depth below letters */
	padding-bottom: .5em;
	/* tab width to right of letters */
	padding-right: .625em;
	/* tab width to left of letters */
	padding-left: .625em;
	/* border between tabs */
	border-right: 1px solid #53A502;
	/* no underline */
	text-decoration: none;
	/* do not change this! */
	display: block;
}

/* Top level link & visited font color */
.slidemenu_class ul li a:link, 
.slidemenu_class ul li a:visited {
	color: #FFFFFF;
}

/* Top level tab background and font color during hover state */
.slidemenu_class ul li a:hover {
	background: #007200;
	/* font color */
	color: #80FF00;
}
	
/* Submenu level items */
.slidemenu_class ul li ul li a {
	/* font styling */
	font-weight: normal;
	font-size: 1.15em;
	font-family: Verdana, Arial, sans-serif;
	/* width of submenu items */
	width: 11em; 
	/* height of submenu items above letters */
	padding-top: .313em;
	/* depth of submenu items below letters */
	padding-bottom: .313em;
	/* border between submenu items */
	border-bottom: 1px solid #006500;
	border-top-width: 0;
	margin: 0;
}

/* Submenu background and font hover colors */
.slidemenu_class ul li ul li a:hover{ 
	background: #007200;
	/* font color */
	color: #80FF00;
}

/* DO NOT CHANGE ANYTHING BELOW THIS LINE! */
/*=========================================*/

/* Top level list - Do not change! */
.slidemenu_class ul {
	margin: 0;
	padding: 0;
	list-style-type: none;
}

/* Top level list items - Do not change! */
.slidemenu_class ul li{
	position: relative;
	display: inline;
	float: left;
}

/* First submenu level - Do not change! */
.slidemenu_class ul li ul {
	position: absolute;
	left: 0;
	display: block;
	visibility: hidden;
}

/* All subsequent submenu levels vertical offset after first submenu level - Do not change! */
.slidemenu_class ul li ul li ul {
	top: 0;
}

/* Submenu level list items (undo style from Top level List Items) - Do not change! */
.slidemenu_class ul li ul li {
	display: list-item;
	float: none;
}

/* Down and right arrow images - Do not change! */
.downarrowclass {
	position: absolute;
	top: 12px;
	right: 7px;
}

.rightarrowclass {
	position: absolute;
	top: 6px;
	right: 5px;
}

/* IE6 hack to get sub menu links to behave correctly - Do not change! */
* html .slidemenu_class ul li a { 
	display: inline-block;
}

The functions to put into custom_functions.php

Following are three functions that you place into your custom_functions.php file. The first function links the menu's CSS file into the head of your post/page HTML document. The second function links the required JQuery file and the JavaScript file into the foot of your post/page HTML document.

The third function actually describes your menu items. It consists of simple unordered HTML lists that become a part of the HTML when your page is rendered, so Google and other search bots will index them. If you choose to create a file from the code below rather than use the zip file, you must name the file "for-custom_functions.php".

/* -----------------------------------------------------------------*/
/* FUNCTIONS FOR THE MULTILEVEL MENU WITH CSS AND JAVASCRIPT */
/* Created by Mike Nichols - July 10, 2009 - Revised Oct 4-17, 2009 */
/* -----------------------------------------------------------------*/

/*--------------------------------*/
/* Link menu css in document head */
function slidemenu_head() {
?>
<link rel="stylesheet" type="text/css" href="<?php bloginfo(template_directory)?>/custom/menu/jqueryslidemenu.css" />

<!--[if lte IE 7]>
<style type="text/css">
html .jqueryslidemenu{height: 1%;} /Hack for IE7 and below*/
</style>
<![endif]-->
<?php
}
add_action('wp_head','slidemenu_head');

/*-------------------------------------------------------*/
/* Call JavaScript and JQuery in footer, set arrow paths */
function slidemenu_foot() {
?>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>

<script type="text/javascript">
var arrowimages={
down:['downarrowclass', '<?php bloginfo('template_url') ?>/custom/images/menu-arrow-down.gif', 23], right:['rightarrowclass', '<?php bloginfo('template_url') ?>/custom/images/menu-arrow-right.gif']
}
</script>

<script type="text/javascript" src="<?php bloginfo(template_directory)?>/custom/menu/jqueryslidemenu.js"></script>
<?php
}
add_action('thesis_hook_after_html','slidemenu_foot');

/*---------------------*/
/* Describe menu items */
function slidemenu(){
?>
<div id="slidemenu_jq" class="slidemenu_class">
<ul>

<!-- First level menu item shows on main menu tab -->
<!-- It is set up as your home tab -->
<li><a href="<?php bloginfo('url');?>">Home</a></li>

<!-- First level menu item with no dropdown -->
<li><a href="#">TAB 2</a></li>

<!-- First level menu item with one dropdown -->
<li><a href="#">TAB 3 WITH 1 DROPDOWN</a>
<!-- this is the dropdown -->
<ul>
<li><a href="#">DROPDOWN 1 ITEM 1</a></li>
<li><a href="#">DROPDOWN 1 ITEM 2</a></li>
<li><a href="#">DROPDOWN 1 ITEM 3</a></li>
<li><a href="#">DROPDOWN 1 ITEM 4</a></li>
</ul>
</li>

<!-- First level menu item with no dropdown -->
<li><a href="#">TAB 4</a></li>

<!-- First level menu item with three dropdowns -->
<li><a href="#">TAB 5 WITH 3 DROPDOWNS</a>
<ul>
<!-- this is the first level dropdown -->
<li><a href="#">DROPDOWN 1 ITEM 1</a></li>
<li><a href="#">DROPDOWN 1 ITEM 2</a>
<!-- this is the second level dropdown -->
<ul>
<li><a href="#">DROPDOWN 2 ITEM 1</a></li>
<li><a href="#">DROPDOWN 2 ITEM 2</a></li>
<li><a href="#">DROPDOWN 2 ITEM 3</a>
<!-- this is the third level dropdown -->
<ul>
<li><a href="#">DROPDOWN 3 ITEM 1</a></li>
<li><a href="#">DROPDOWN 3 ITEM 2</a></li>
<li><a href="#">DROPDOWN 3 ITEM 3</a></li>
<li><a href="#">DROPDOWN 3 ITEM 4</a></li>
<li><a href="#">DROPDOWN 3 ITEM 5</a></li>
</ul>
</li>
<li><a href="#">DROPDOWN 2 ITEM 4</a></li>
</ul>
</li>
</ul>
</li>

<!-- First level menu item with one dropdown -->
<li><a href="#">TAB 6 WITH 1 SUBLEVEL</a>
<!-- this is the dropdown -->
<ul>
<li><a href="#">SUBITEM 1</a></li>
<li><a href="#">SUBITEM 2</a></li>
</ul>
</li>
</ul>
<br style="clear: left" />
</div>
<?php
}
remove_action('thesis_hook_before_header', 'thesis_nav_menu');
/* YOU MUST UNCOMMENT ONE OF THESE ADD_ACTION LINES! */
/* PLACE THE MENU BEFORE THE HEADER */
/*add_action('thesis_hook_before_header', 'slidemenu');*/
/* PLACE THE MENU AFTER THE HEADER */
/*add_action('thesis_hook_after_header', 'slidemenu');*/

The images for the right and down arrows

There are two small image files for the right and down arrows in the menu. They look like this:

Figure 1: The right arrow image

Figure 4: The right arrow image


Figure 2: The down arrow image

Figure 5: The down arrow image

That's it! The following section tells you in detail how to set up the menu.

Setting up the Multilevel Menu with CSS and JavaScript

There are 3 steps for setting up your menu. Each step is not difficult, but the instructions must be followed to the letter. Most of the problems people experience when setting up the menu are because they skip a step or do not follow the directions.

Step 1: Set up a folder and copy files

Figure 6: Custom folder after creating the menu folder and copying files

Figure 6: Custom folder after creating the menu folder and copying files

1. Create a new folder named "menu" in your /custom folder.
2. Copy "jqueryslidemenu.js," "jqueryslidemenu.css" files into the new menu folder. If you downloaded the zip file, they are contained in a folder named "put-these-in-menu-folder." Also included in the zip file is "jqueryslidemenu-thesis.css" which mimics the Thesis standard navigation bar. If you want to try this out, it should go into the menu folder, too.

When you finish your custom folder should look like Figure 6.

Step 2: Upload the arrow images

Upload both arrow images to your /custom/images folder. They are named "menu-arrow-right.gif" and "menu-arrow-down.gif". If you downloaded the zip file, they are both in the folder named "put-these-in-custom-images-folder."

Step 3: Put the three functions into your custom_functions.php file

1. Copy and paste the three functions in the file "for-custom_functions.php" into your custom_functions.php file. It does not matter where these functions go. (Note that the "for-custom_functions.php" file is not uploaded to your server.)
2. At the bottom of the third function are two "add_action" statements. Important: Uncomment the one you want to use -- if you don't the menu will not appear and you will have no menu at all!

You're finished with the setup. Now you're ready to try it out!

How to Set Up Your Menu Items

The menu is just an HTML unordered list with the following structure. Adding menu items is simple, but you have to follow instructions carefully:

Main Menu Tabs
	First Level Dropdown Items
		Second Level Dropdown Items
			Third Level Dropdown Items
				...

1. Basically, you will be doing two things when setting up a menu item: putting in the item's URL, and putting in its menu description. Figure 7 shows a line before it has been set up.

Figure 4: Changing the menu items in the function

Figure 7: Changing the menu items in the function

2. The URL for your menu item replaces the pound sign (#). The description for the menu item -- the word(s) that actually show on the menu -- replace the example words. For example, if I am setting up Thesis Theme Tool's About page, it would look like the when I am finished. Remember that you can put any URL into any menu item:

<li><a href="http://thesisthemetools.com/about/">About</a></li>

3. Adding more menu items follows the same procedure. Just replace the pound sign with your URL and the example description with your description.
4. Note that the first tab is already set up as your home tab. You may change it if you wish.

Figure 5: Nesting of unordered lists in the function

Figure 8: Nesting of unordered lists in the function

5. When editing dropdowns, make sure that you observe the way the lists are constructed: The unordered list for dropdowns come right before the line item's "</li>" tag. This tag is placed after the end of the unordered list. In Figure 8 you will see the nesting of the tags represented by red lines.

Adding a Single Tab Item with No Dropdown

1. Copy a line with no dropdowns, such as the Home page line, "TAB 2" or "TAB 4" lines.
2. Paste the line in the code where you want the menu item to appear.
3. Replace the URL or pound sign with your URL, and the description with your description.

Adding a Single Tab Item with One Dropdown

1. Copy a group of lines with a single dropdown, such as the "TAB 3" or "TAB 6" lines. Be sure to copy all the lines associated with the tab.
2. Paste the lines in the code where you want the menu item to appear.
3. Replace the URLs or pound signs with your URL, and the descriptions with your descriptions.

Adding a Single Tab Item with Multiple Dropdowns

1. Copy a group of lines with a multiple dropdowns, such as the "TAB 5" group. Be sure to copy all the lines associated with the tab.
2. Paste the lines in the code where you want the menu item to appear.
3. Delete any submenus that you don't need. Be sure to delete all the lines associated with the unneeded submenus, but observe the menu's structure and don't delete too many!
4. Replace the URLs or pound signs with your URL, and the descriptions with your descriptions.

Deleting or Rearranging Menu Items

Menu items can be deleted or rearranged easily,
1. Cut the line or group of lines associated with the menu item. Make sure you get all the lines, but be sure you don't cut the lines of other groups! If you are unsure of which lines to cut, take a look at Figure 8 again.
2. If you are moving the menu item, find the place you want it and paste it there.

Usage tips for the Multilevel Menu with CSS and JavaScript

1. The menu lines have been commented and indented in the code to better show you the menu structure. I suggest you not erase these lines to make working with the menu easier.
2. Keep the file "for-custom_functions.php" so you can copy its menu lines when you need them.
3. Be careful about long menu descriptions for dropdowns. If they are too long, they may get cut off in the dropdown.
4. Remember that browsers with JavaScript turned off will only see the main menu tabs. You need to make these main menu tabs point to the more important areas of your site, and/or go to pages that have links to the dropdown's items.
5. If you want to see the version of the menu that mimics the Thesis standard navigation bar:

  • Make sure the "jqueryslidemenu-thesis.css" file is in your /custom/menu/ folder. If it isn't, copy it there.
  • Rename the "jqueryslidemenu.css" in your /custom/menu folder to something like ""jqueryslidemenu-green.css"
  • Rename the "jqueryslidemenu-thesis.css" file to "jqueryslidemenu.css"

This just changes the appearance of the menu. It doesn't change any of the contents of the menu, and all your menu items will still work.

Troubleshooting Your Menu

1. The menu does not appear

You did not uncomment one of the lines at the end of the "slidemenu" function. The look like this:

/* YOU MUST UNCOMMENT ONE OF THESE ADD_ACTION LINES! */
/* PLACE THE MENU BEFORE THE HEADER */
/*add_action('thesis_hook_before_header', 'slidemenu');*/
/* PLACE THE MENU AFTER THE HEADER */
/*add_action('thesis_hook_after_header', 'slidemenu');*/

2. The menu does not appear

Check the file path to the "jqueryslidemenu.js" in the "slidemenu_foot" function. It looks like this:

	<script type="text/javascript" src="<?php bloginfo(template_directory)?>/custom/menu/jqueryslidemenu.js"></script>

There are two main causes for this:

  • You named your menu folder something besides "menu." Just change the "menu" folder in the URL to the name of the folder you created.
  • Your server has a non-standard file setup. The "bloginfo" variable will find most standard Thesis setups, but you may have to put the full file path to the "jqueryslidemenu.js" file into the URL.

3. The menu appears as a vertical list on the left side of the screen

The solution to this problem is almost identical to item 2, above. The reason is because the program is not finding your "jqueryslidemenu.css" file. Check the file path to the "jqueryslidemenu.css" in the "slidemenu_head" function. It looks like this:

	<link rel="stylesheet" type="text/css" href="<?php bloginfo(template_directory)?>/custom/menu/jqueryslidemenu.css" />

Like item 2, there are two main causes for this:

  • You named your menu folder something besides "menu." Just change the "menu" folder in the URL to the name of the folder you created.
  • Your server has a non-standard file setup. The "bloginfo" variable will find most standard Thesis setups, but you may have to put the full file path to the "jqueryslidemenu.css" file into the URL.

4. Nothing happens when I click on a menu item, or all that appears in the browser address is a pound sign

You haven't set up the menu correctly, or you didn't delete unneeded menu items. Review the instructions in the "How to Set Up Your Menu Items" section above.

5. The menu moves too fast for my mouse to go to dropdown items, or the menu "stutters"

Usually this is caused by the JavaScript drawing and undrawing the menu too fast. Go to the "jqueryslidemenu.js" file in your menu folder. At the top of the file are some settings for how fast the menu draws. They look like this:

//Change the duration of the slide in/out animation - in milliseconds
animateduration: {over: 200, out: 100}, 

In particular, you might want to change the "out" parameter to a larger number. It will keep the menu from disappearing before your cursor has a chance to move over it.

6. The menu looks like "slats" in Internet Explorer

Usually this is due to padding and margin issues in the "jqueryslidemenu.css" file. IE interprets these differently from every other browser. Unfortunately, this doesn't happen with everybody's copy of IE -- just on some of them. You may want to experiment with the padding and margin values of the second level selectors to see if it solves your problem.

7. The menu does not let me pull the dropdown all the way down in Internet Explorer

This usually is due to the menu moving too fast for Internet Explorer. Try the solution in item 5, but make both the "in" and "out" variables a larger number.

Remember: rather than copy the files shown in the examples, I recommend you get everything -- all files and the arrow graphics -- in a single zip file. You can download it by clicking here.

I hope you try out the Multilevel Menu with CSS and JavaScript. It works very smoothly, and has been trouble-free for me on Thesis Demo. As always, your comments are welcome. You can also contact me by email by clicking the "Contact" button at the top of the page.

©2009 Michael L Nichols. All rights reserved.

What next?

Your comments are always welcome, and are important to this blog's community! Leave a comment now, or read the comments.

You can find several related articles in the "Related Articles" list below. In the footer you will find a lists of Popular Posts, Recent Posts, and you may browse by Categories, or tags. There's also a Google Custom Search box to help you find just what you want.

Get free updates by RSS or email!

If you have enjoyed this article, please consider subscribing to article updates, using an RSS reader, or by email. It's free and is a great way to make sure you don't miss a single article! I also invite you to follow me on Twitter!

Why not share this article with others!

Share this article with your friends using your favorite social media service, such as StumbleUpon, or Digg. Check out the icons below under "Share This Article With Others" for other social media, including del.icio.us, Technorati, Sphinn, Friendfeed, FaceBook, MySpace andLinkedIn! You can also email or print the article, and even tweet it using Twitter!

Be Sociable, Share!