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. ARTI
    ARTI says:

    It works fine on my version of Firefox 1.5.0.1 Perhaps you don’t have the latest version. I run an image gallery myself, but I don’t think type of layout would artı work very well for me as I have descriptions for my images and some of the images are quite large. I don’t know, it might be worth experimenting with.

  2. Meeesta
    Meeesta says:

    Firstly Kriesi, big thanks to you, I looked for ages trying to find a robust dynamic menu system, and this one fit the bill perfectly.

    Just one thing, is there any compatibility issues with newer versions of jQuery? I tried switching to 1.3.2 but couldn’t get the menu to work.

  3. Philippe
    Philippe says:

    hidden is calculated was changed in jQuery 1.3.2 (see http://docs.jquery.com/Selectors/hidden and http://docs.jquery.com/Release:jQuery_1.3.2#:visible.2F:hidden_Overhauled)

    The code below works fine for me now :
    $(“#menuBar li”).hover(function(){
    $(this).find(‘ul:first:hidden’).css({‘visibility':’visible’,’display':”none”}).fadeIn(400);
    },function(){ $(this).find(‘ul:first’).css({‘visibility':’hidden’,’display':”none”});
    });}

    I added ‘display':”none” when rollout

  4. Govindji Patel
    Govindji Patel says:

    Hello

    I like the tutorial and have it working on firefox and I am still having problem for it to work on IE when mouseover nothing happens just highlights the tab.

    I read that some posters did have it working on IE I would be very glad if someone who has it working help.
    I have it here on a test website that is a phpnuke CMS
    http://www.phpnukeblog.com/html

  5. thijs
    thijs says:

    Nice tutorial.
    I found that in FF 3.x, the Opera fix that you put in has the effect that when you move the mouse out of the top LI element, the sub menu is hidden again. I don’t get why that is, because if you are hovering over the sub menu, strictly you are hovering over the LI as well.
    I also did not really notice the Opera problem the fix was intended for.

    When I changed the fix to the following, it worked again:

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

    Considering that nobody else commented about this, I must have another css issue which makes the sub menu disappear too soon. I’m building this into an existing site, so it’s not as ‘clean slate’ as I’d like.
    As soon as I have it up on my domain, I’ll drop the link so you can take a peek.
    All in all, thanks for this.

  6. Gavin
    Gavin says:

    For those having issues with jQuery 1.3.2 it’s b/c they changed how jQuery sees things as hidden see here

    The menu was just showing up once for me, but once the visibility was changed to hidden it wouldn’t appear again. I just removed the :hidden portion and all worked fine.

  7. Ed Ruder
    Ed Ruder says:

    Gavin’s fix for jQuery 1.3.2 worked for me, too–change

    $(this).find(‘ul:first:hidden’)

    in the first function passed to the hover function to

    $(this).find(‘ul:first’)

  8. Srky
    Srky says:

    After i saw that lot of people asked how to create with highlighted previous levels and lot of them gave half of answers i created full XD.

    Link: http://rapidshare.com/files/235497431/dropdown.zip //virus free
    Tested and working on: Firefox 3.0, 3.1b3, Internet Explorer 6, 7, 8, Opera 9.0, 9.6 and Safari 3.0 on XP SP3

    P.S. Plz Kriesi if you could upload on your website.

  9. chinalwb
    chinalwb says:

    Thanks a million, pals,

    SMASHING menu, I’d say.

    I am enjoying this menu, it does help me a lot, however, here is a problem of mine:
    How can I add a down-arrow for the vertical menus, and add a right-arrow for the horizontal menus?

    I’d like to express my gratitude, thanks heaps!

    Best Regards,
    chinalwb

    2009-05-26 09:30 AM

  10. Brigitta
    Brigitta says:

    Hi there,

    I was very happy when I found your page, this menu is just what I needed, very smooth and compact. Thank you for sharing!!

    Brigi.

  11. sisi
    sisi says:

    Hi

    Thank you very much for this work. It is great and very useful.

    I have the same problem than Tomas on October 21st, 2008 “if you get out of the menu over a input=text the menu does not roll off (disappear)”

    In my case, it is not a small text field but a big textarea.

    This code fixed the problem for me:

    We create an empty div before the nav menu code

    with this style:

    #bgTransparent{
    display:none;
    background:transparent;
    position:fixed;
    height:100%;
    width:100%;
    top:0;
    left:0;
    z-index:1;
    }

    #nav, #nav ul {

    […]
    z-index:2;
    }

    This is my jquery code:

    function mainmenu(){

    $(” #nav ul “).css({display: “none”}); // Opera Fix
    $(” #nav li “).hover(function(){
    $(‘#bgTransparent’).show();
    $(this).find(‘ul:first:hidden’).css({visibility: “visible”,display: “none”,opacity: “0.9”}).slideDown(“400″);
    },function(){
    $(this).find(‘ul:first’).slideUp(“400″);
    $(‘#bgTransparent’).hide();
    });
    }
    $(document).ready(function(){
    edHeadings();
    });

    Thank you again.

  12. Benji64
    Benji64 says:

    Hi Guys,

    I have managed to modify the script to meet my design but i am having a problem in ie6 and 7 with the menu going off to the right off the text when the menu expands? works fine in all other browsers. Cheers for your help.

    Benji

  13. Musta
    Musta says:

    This is really superelegant, I will use it, for sure, in one of my future projects. What I like most about this is that the menus are kept visible even when the javascipt is disabled, which is very important for any website that takes accessibility into account.
    Thanks for the tutorial.

  14. Christian
    Christian says:

    Here is my code:

    function mainmenu(){
    $(“#navList > UL”).css({display: “none”}); // Opera Fix
    $(“#navList LI”).hover(function(){
    $(this).find(‘UL:first:hidden’).css({‘visibility':’visible’,’display':”none”}).show(400);
    },function(){ $(this).find(‘UL:first’).css({‘visibility':’hidden’,’display':”none”});
    });}

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

    On Firefox 3.5 the first time you point at a link that has a submenu the submenu flashes on instead of being “shown” jQuery style. Every subsequent time however the submenu fades in to view correctly. Any suggestions on how to get it to work right the first time? Also, it seems that once the submenu is fully in view it shifts to the right a pixel or two.

  15. Ben
    Ben says:

    I really like this menu, I was just wondering if there is any way this menu can be implemented with your apple style menu as well.

    Best regards,
    Ben

  16. Jonathan
    Jonathan says:

    Hi, really like the menu and have implemented it into a site I’m currently working on. I just can’t get the jquery to work though.
    I’m using asp master pages. Is there any reason this might be a problem?

    Cheers

  17. Denis
    Denis says:

    I’d sure like to replace my current script with yours. Where do I find info on running two menus on one page? That’s the problem I’m having since the css seems designed for a single menu.

  18. squirrel
    squirrel says:

    Hi, I can’t get this to work in IE6. Please look at http://atribuut.ee/minig-kolmas-menuupunkt/
    Try to hover over “Sisumallid” and then try to move cursor to 2nd or third menu tiem. The menu disappears. I cannot undesratnd what is causing this. It seems however that when I push down the content div, ie give it a huge top margin, the menu will not dissapear anymore. I tried all kinds of position:relative and z-index tricks but I coudlnät make it work. Matbe someone here can help?

  19. squirrel
    squirrel says:

    Alright, I figured it out. Took me about 4 hours arghhh.. DIE IE6!!!!

    The trick is that You need to have a background-color for the dropdown menu ul. It’s not enough if the li or a has a background-color.

  20. Eric
    Eric says:

    Excellent work on this menu and the explanation and support you’ve been giving. Is it possible to add different effects to different levels of the menu? For example, the first level slides out and its children fade in.

    Thanks and keep up the great work.

Trackbacks & Pingbacks

« Older Comments

Comments are closed.