css

Create a multilevel Dropdown menu with CSS and improve it via jQuery

Some of you might have noticed, I have a partiality for sleek menus. As I recently had to create a multi level dropdown menu for one of my customers, I wanted to improve it with a little bit of jQuery, but couldn’t find a script that accomplished what I needed.

So I decided to build this menu from scratch and share my thoughts as well as the code with you.

So before we start: this is what we are going to build


The first part of this tutorial is dedicated to the task of building a working CSS-only dropdown menu (also known as suckerfish menu), the second part will show you how you can pimp the whole thing with a few lines of jQuery.

The CSS-only menu is cross browser tested and from what I can tell works with all browsers except for IE6.
The Internet Explorer needs the addition of our jQuery function to work properly.

To create a CSS-only dropdown menu that works without Javascript (even in IE6), you need tons of extra markup and CSS, if you really need this for any reason check out Stu Nicholls CSSplay, he addresses this problem with heavy (ab)use of conditional comments =)

Now that you have a little bit of background information, lets create our own menu:

First of all we need the XHTML Structure of our soon-to-be terrific menu. We will use a nested list for this purpose, top level list id is “nav”:

<ul id="nav">
    <li><a href="#">1 HTML</a></li>
    <li><a href="#">2 CSS</a></li>
    <li><a href="#">3 Javascript</a>
        <ul>
            <li><a href="#">3.1 jQuery</a>
                <ul>
                    <li><a href="#">3.1.1 Download</a></li>
                    <li><a href="#">3.1.2 Tutorial</a></li>
                </ul>
            </li>
            <li><a href="#">3.2 Mootools</a></li>
            <li><a href="#">3.3 Prototype</a></li>
        </ul>
    </li>
</ul>

Thats it for the HTML part; without CSS styling our menu looks like this: Step 1

Now for the stylesheet part:

#nav, #nav ul{
     margin:0;
     padding:0;
     list-style-type:none;
     list-style-position:outside;
     position:relative;
     line-height:1.5em;
 }

This removes the indents browsers tend to make, as well as the bullets from #nav and all its child-ul elements. The “position:relative” is needed since we will arrange some of the contained elements with position:relative and absolute. This is necessary since relative and absolute positioned elements are positioned according to their containing blocks with a position attribute, other then static.

Line-height defines the height of each list item. You could set the height attribute for your list-items to define their height, but line-height will center the link text vertically without the need to play with margins and paddings.

 #nav a:link, #nav a:active, #nav a:visited{
    display:block;
    padding:0px 5px;
    border:1px solid #333;
    color:#fff;
    text-decoration:none;
    background-color:#333;
 }

#nav a:hover{
    background-color:#fff;
    color:#333;
}

This one is pretty straight forward:
it will style each hyper link in our menu a little bit. At this time the menu looks like this: Step 2

Now lets add some more styles:

#nav li{
    float:left;
    position:relative;
}

This will align our list elements horizontally.

#nav ul {
    position:absolute;
    width:12em;
    top:1.5em;
    display:none;
}

This will position the nested Lists right beyond the main menu and give them a width of 12em. The width attribute is needed so that the list items within display vertically again. The Top attribute should have the same value as the line-height attribute we defined for #nav.

#nav li ul a{
    width:12em;
    float:left;
}

This will set the width of the hyper links to 12 em (which in combination with the width of the UL set above results in a horizontally displayed sub menu, despite of the ongoing float:left)

#nav ul ul{
	top:auto;
	}

#nav li ul ul {
    left:12em;
    margin:0px 0 0 10px;
    }

#nav li:hover ul ul, #nav li:hover ul ul ul, #nav li:hover ul ul ul ul{
    display:none;
    }
#nav li:hover ul, #nav li li:hover ul, #nav li li li:hover ul, #nav li li li li:hover ul{
    display:block;
    }

#nav ul ul and #nav li ul ul define where we display the sub menus.

The hover states define which items we want to show when hovering over an item (only the next sub level, not all of them)

After applying these styles we got this menu: Step3

If you are working with a browser other than IE6 you should have a basic dropdown menu now.

Gogo jQuery!

So lets spice up the menu a little bit. First of all here is the whole jQuery Code I used to create the effect:

function mainmenu(){
$(" #nav ul ").css({display: "none"}); // Opera Fix
$(" #nav li").hover(function(){
		$(this).find('ul:first').css({visibility: "visible",display: "none"}).show(400);
		},function(){
		$(this).find('ul:first').css({visibility: "hidden"});
		});
}

 $(document).ready(function(){
	mainmenu();
});

Step by step description:

$(" #nav ul ").css({display: "none"}); // Opera Fix

This is a small fix for Opera, which doesn’t hide the menus fast enough, if you hover above them and so creates a flickering effect. $(” #nav ul “) is the jQuery way to select all unordered lists in #nav. Usage of this is similar to selecting CSS elements. The .css({display:”none”}) sets the display attribute for all Unordered lists to none.

$(" #nav li").hover(function(){ // here goes mouse over effect
                  },function(){ // here goes mouse out effect
                               });

This is the jQuery function for hovering. Really simple to use: first function lets you define what happens when you hover over a specific item( in our case a list item), second function is used for the mouse out event.

$(this).find('ul:first:hidden').css({visibility: "visible",display: "none"}).show(400);

This function finds the first hidden unordered list within the currently hovered list item and shows it. The function “.show” works only under specific circumstances, this is why we set the display to none. The number between the braces defines the animation speed in milliseconds.

$(this).find('ul:first').css({visibility: "hidden"});

This is the mouse out event: we use visibility instead of display since the show function mentioned above, sets display to “block” at the end of the animation. This way if you would hover just a short moment over the item the item would not display for the ongoing animation and then pop out all of a sudden. Using visibility prevents this flickering.

 $(document).ready(function(){
	mainmenu();
});

This function will call our mainmenu() function as soon as the html document is ready.

We are done now, if you can’t get this to work for any reason, here is a working example for download:

Download
Includes: HTML File, CSS File, Javscript Files

Have fun creating your own menu ;)

Update :

I have added some small fixes to the script which were suggested by some of my readers, so thanks for adding improvements. ;)

Another thing I wanted to note: I have seen this script in many templates and wordpress themes now, often used in conjunction with one of wordpress automatic list generating features (wp_list_categories, wp_list_pages)
If you use it that way wordpress adds a title attribute to each link, which can create a flickering effect when the browser tooltip for the title appears. If you are using the script this way you should add the following line of jquery at the beggining of the script to remove the titles:

$(" #nav a").removeAttr("title");

(Last updated 18.09.2008)

Tags: , ,
398 replies
« Older CommentsNewer Comments »
  1. kasparcho
    kasparcho says:

    Srky,
    Please contact me through my mail: kasparcho@mail.bg or skype: a_tsonev
    I found a problem with a little modification of your script.
    Thanks in a advance.

    Special thanks to Kleisi for the creation
    and Srky for the update of this wonderful script.

  2. sannie
    sannie says:

    lighting up the current page

    hi kriesi,

    thanks very much for this brilliant piece of work.
    I wanted to share this nice usefull feature
    for lighting up the current page in the menu.

    maybe in addition to this you know how to make the dropdown of the current page in the same color as the not-current pages when visiting the current page. in my case it’s pink so that’s a lot of pink dropping down.

    great tutorial btw.

    sannie

    page has to have extention .php

    add to css:

    #nav #currentpage a {
    background-color:#FF0099;
    color:#fff;}

    #nav #currentpage a:hover{
    background-color:#fff;
    color:#333;
    }

    add to top of page:

    add php to menu:

    <li>
    about us
    <li>
    models

    models

    a

    b

    c

    d

    e

  3. Ulises
    Ulises says:

    I have to say that I found your explanation very helpful.. even though I am not using this tecnology(the menu) for my site…

    I have a question… Even though the menu works perfectly.. the unordered listed could include images to make the menu look smoother visually.. Isn’t that right?

    actually I am going to change my web partly using jquery UI.. adding some glitz(ehhe)

  4. Tyler
    Tyler says:

    I use z-index to make it works in ie6 ( if not the menu feet appear below the content that follows it ) :

    1. On the ul #nav :
    #nav {
    position:absolute;
    top:79px;
    z-index:100;
    }

    2. On the content ( following my menu ) :
    #content {
    position:relative;
    width:100%;
    margin-top:30px;
    z-index:-10;
    }

    But you HAVE TO specify the “position” ( relative, absolute or fixed ) for both elements #nav and #content.

  5. Uday
    Uday says:

    I have created the menu of the same kind. I have called JavaScript functions onclick of the hyperlink of menu items. But now the menu doesn’t disappear when i click the hyperlink. Can you tell me where i may have gone wrong or this is the way it is?

  6. Don
    Don says:

    Thanks very much for your d/d/menu I have been trying for some time to achieve this. I am trying to convert your code to my website. I have a couple of problems though; the first one is that the border on the first level is being overwritten. I have tried many ways to solve this can you help. you can see the result at:-
    http://dl.dropbox.com/u/2701238/dropmenu1.jpg
    The next problem is that as IE does not support position: absolute any ideas as to how I can reposition the menu in IE as it shifts to the right. Just remembered another problem, the first drop level gets selected before I reach the menu bar from below. How can I stop this.

  7. Matt Mahar
    Matt Mahar says:

    When doing the css hover section, you added in an unnecessary entry to both the display and the hide portions.

    It could be left as just this:
    #nav li:hover ul ul, #nav li:hover ul ul ul{
    display:none;
    }
    #nav li:hover ul, #nav li li:hover ul, #nav li li li:hover ul{
    display:block;
    }

    Notice I took out the last entry on both items. This was giving me fits because I was looking for unordered lists that weren’t there. But when using this in the css, it works the exact same. Now, if you wanted to add another sub menu to the “download” tab, then you would need the extra lines of css code. I’m not sure why you added in extra css when you didn’t need to unless you anticipated people needing more than 3 layers. I think your tutorial is awesome but PLEASE, when you add a line to css code that is not necessary for this specific tutorial, please let us know since I was pulling my hair out and questioning my css intelligence trying to find the extra unordered list. Having said that , really nice tutorial.

  8. Matt Mahar
    Matt Mahar says:

    Upon further review, it seams you added two(2) unecessary lines of CSS code. The drop down, css only code shows fine with this:

    #nav li:hover ul ul{
    display:none;
    }
    #nav li:hover ul, #nav li li:hover ul{
    display:block;
    }

    Note what I did here. Since the #nav id already calls the main “ul”, you only need to tell it to not show the 3rd level(or third ul). On the second one, you only need to tell it to show the second and third level when hovered. The only reason I can see that you added the extra CSS is incase you want more levels, the CSS is already written for you. You comment is much appreciated since it was driving me nuts. I questioned my whole understanding of CSS.

  9. Glyn
    Glyn says:

    @jwright – Not sure if you sorted the setTimeout issue. I too am a newbie to JS but was able to hunt around and sort out the delay after falling off the menu.

    Here is my amended JS

    function mainmenu(){
    $(” #nav ul “).css({display: “none”}); // Opera Fix
    $(” #nav li”).hover(function(){
    $(this).find(‘ul:first’).css({visibility: “visible”,display: “none”}).show(300);
    },

    function (){
    var el=this;
    setTimeout(function(){

    $(el).find(‘ul:first’).css({visibility: “hidden”});
    }, 2000)

    }

    );
    }

    $(document).ready(function(){
    mainmenu();
    });

  10. Glyn
    Glyn says:

    OK my problem with what I juts posted is that when I hover over a few secondary menu items the tertiary menus hang around.

    How do I say only delay the “hidden” whn I hover off all menus?

    Cheers

  11. Lee89757
    Lee89757 says:

    Hi, thanks for this tutorial. it works really great. But i have problem to put the menu to a menu container(inside i put menu here). menu appear outside the container. can someone give me solution. Thanks

  12. Tarun Madhok
    Tarun Madhok says:

    very nice tutorial!!

    but it cant helped me out !!!
    iam using wordpress

    i already have dropdown menu. i want to extend to flyout for sub-submenu

    I am desperately need help!!

  13. Damrod
    Damrod says:

    First of all… great tutorial THANKS! :)

    And now the question ;)
    How do I add an indication on the FIRST level if the user is clicking on a link?
    An addClass(“selected”) on the click-event is not enough in my case, I want to add the “selected” class to the parent element on level 1. See example below:

    [-1-] [-2-] [-3-]
    [-3a-]
    [-3b-]

    Let’s say the user is clicking on submenu item “3b”, I would like to highlight menu item 3.

    Hope you can help.
    Thanks,
    Damrod

  14. Meryl
    Meryl says:

    I love your menu, and I’m using it for my personal Website. I’m having one problem – in Firefox.

    You can see my site and the problem here: http://www.merylpaul.com/merylIndex2.shtml

    I have two items that I want to drop down from the main menu. The first one (classwork) drops down fine. But the other (photography) doesn’t show in Firefox. It should have two items dropping down: Spring-Summer Gallery and Fall-Winter Gallery.

    The menu works fine in IE7, just not in Firefox.

    Was the menu only supposed to allow one drop-down?

  15. Adam
    Adam says:

    Excellent tutorial. this is the first thing i have worked on using jquery and had it up and running in minutes! bye bye ASP menu at last!

Trackbacks & Pingbacks

« Older Comments

Comments are closed.