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

    The second level menu (3.1.1 & 3.1.2) is displayed buggy in new IE 8 (beta).

    Any ideas how to work around this?

  2. Vincent Le Pes
    Vincent Le Pes says:

    I was having major issues using your method because if you scroll over a link back and forth it queues up animation, resulting in the box fading in and out over and over. Sometimes it would flicker as my cursor was right on the edge of the button, queuing up a lot of animation. After playing around, I found a very easy way to check if the animation was already happening without adding any if statements or timeout functions. All I had to do was check to see if the menu was already visible and only apply the .show animation to hidden elements using the :hidden selector. I would recommend the following simple change for anyone else experiencing this problem:

    Simply change this line:

    $(this).find(‘ul:first’).css({visibility: “visible”,display: “none”}).show(400);

    to this:

    $(this).find(‘ul:first:hidden’).css({visibility: “visible”,display: “none”}).show(400);

    I hope some of you find this helpful, and maybe you could even add it to your tutorial.

  3. Kriesi
    Kriesi says:

    Sounds like a good solution to me although I wasn’t able to reproduce this behavior. When I scroll over the links the animation doesn’t queue up. Which OS, browser and jQuery version are you using?

  4. Vincent Le Pes
    Vincent Le Pes says:

    I had the issue in FF3 and IE6/7, all running on XP SP2. I had some modifications to the styles that may have something to do with it, I don’t know for sure but I’d bet others are having this problem as well. I think it started happening when I switched the animation to fadeIn, but I’m not sure. The final version of mine is below, if you’d like to test it out. I used class=”nav-horiz” instead of id=”nav” and in some cases styled for menus only within the header, as I will be using this script in multiple places for my current project. I hope you guys find this useful:

    ———-JS———-

    function mainmenu(){
    $(” .nav-horiz ul “).css({display: “none”}); // Opera Fix
    $(” .nav-horiz li “).hover(function(){
    $(this).find(‘ul:first:hidden’).css({visibility: “visible”, display: “none”}).fadeIn(400);
    },function(){
    $(this).find(‘ul:first’).fadeOut(100).css({visibility: “hidden”, display: “block”});
    });
    }

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

    ———-CSS———-

    /*— Horizontal Navigation Bar —*/

    #header .nav-horiz {
    position: absolute;
    top: 90px;
    left: 0px;
    width: 760px;
    margin: 0px 10px;
    background: #43071a url(../images/nav.jpg) repeat-x center top;
    border-top: #43071a solid 1px;
    border-bottom: #43071a solid 1px;
    }
    .nav-horiz, .nav-horiz ul {
    position: relative;
    width: 100%;
    margin: 0px;
    padding: 0px;
    line-height: 35px;
    list-style: none;
    }
    .nav-horiz a:link,
    .nav-horiz a:active,
    .nav-horiz a:visited {
    display: block;
    padding: 0px 18px;
    border-left: #a04e68 solid 1px;
    border-right: #43071a solid 1px;
    color: #fff;
    text-decoration: none;
    }
    .nav-horiz a:hover {
    color: #cdcdcd;
    background: #720D2D;
    }
    .nav-horiz li {
    float: left;
    position: relative;
    }
    .nav-horiz ul {
    display: none;
    position: absolute;
    top: 35px;
    width: 182px;
    background: #720D2D;
    border-top: #a04e68 solid 1px;
    border-bottom: #43071a solid 1px;
    }
    .nav-horiz li ul a {
    float: left;
    width: 145px;
    line-height: 35px;
    }
    .nav-horiz li ul a:hover {
    background: #43071a;
    }
    .nav-horiz ul ul {
    top: auto;
    }
    .nav-horiz li ul ul {
    left: 173px;
    margin: 0px 0px 0px 10px;
    }
    .nav-horiz li:hover ul ul,
    .nav-horiz li:hover ul ul ul,
    .nav-horiz li:hover ul ul ul ul {
    display: none;
    }
    .nav-horiz li:hover ul,
    .nav-horiz li li:hover ul,
    .nav-horiz li li li:hover ul,
    .nav-horiz li li li li:hover ul {
    display: block;
    }

  5. Kriesi
    Kriesi says:

    Hey Vincent,
    I was able to reproduce the bug with your script. The multiple fade ins happen because you are using the fadeOut function.

    To have a real leave animation you have to alter the css as well since on leaving with the cursor, the css instantly kicks in and hides the sub menu items, just before jQuery can show the leave animation.
    Therefore the fadeOut is useless at the current state.

    Nevertheless I like your fix since it seems to be a good start to make such an animation possible ;)

    Another thing I just learned is that the animation always queues up but with no animation for mouse leaving that doesn’t matter. Your addition to the code fixes this as well. The whole script becomes a little better through this, so thanks for your suggestion ;)

  6. Vincent Le Pes
    Vincent Le Pes says:

    No problem, glad I could be of help. I am new to jQuery and taking a crash course at my job, and so far it’s been very exciting. Tutorials like this and sites like yours have been an indispensable resource, keep up the great posts.

    I also noticed the css was happening immediately and ruining the fadeout as well, but I’m not sure how to make the css change queue up after the animation.

  7. Kriesi
    Kriesi says:

    shouldnt be that hard..

    I guess changing

    $(this).find(’ul:first’).fadeOut(100).css({visibility: “hidden”, display: “block”});

    to

    $(this).find(’ul:first’).fadeOut(100);

    should work

  8. Charlie
    Charlie says:

    Hi Kriesi,

    First of all Thanks!!!! That is one of the best tutorials i have come across!! I am very much a novice at this and i was hoping you might have the time to explain in greater detail how to convert this over to a vertical menu. I have managed to set the menu up so it sits vertically, but to get the sub menus to slide out to the right… i am at a bit of a loss.

    Many Thanks for any help you can lend
    Charlie

  9. Noor
    Noor says:

    Hi,
    It really cool I have read many tutorial to creating drop down menu I have found this tutorial best of them all thanks for providing such a nice stuff keep it up

    Noor

  10. Ebrahim
    Ebrahim says:

    Excellent plugin, just one thing…

    Is it possible to give a the parent menu active class while the dropdown menu is still open?

    Currently, parent menus are only regular links with hover styling, as soon as the mouse is moved away, it returns to normal state.

    Thanks

  11. Sara Rao
    Sara Rao says:

    Hi Kriesi,
    I have worked very hard on developing a website. I made it to look good with the drop down menus working and everything looking good at any screen resolution in both Firefox and IE 7. Then I tested it on IE 6 and everything fell apart. The menu drop downs get hidden behind other div’s and the horizontal menu’s do not line up the way they should. I want to send u my files and images as an attachment as I currently do not have url to give to you as I haven’t uploaded my files on to a webserver. Could you please provide me with an Email address where I can send the html, css and image files as well as snapshots of what the page looks like in IE 7 and IE 6. Please help me as this is an important school project for me. Thanks.

  12. OZ
    OZ says:

    how was jonathan’s problem solved? I am having the same issue; my menu falls behind another element in the page.

  13. Tomas
    Tomas says:

    Hi,

    I am using similar menu in our (inhouse) system and we discovered a bug in FF – if you get out of the menu over a input=text the menu does not roll off (disappear).

    in this case just place a input text underneath the menu and make “mouse out” over it and the menu does not disappear, but remains opened.

    I am trying to solve it but no luck so far. maybe you (all people) get any idea :)

    thanks.

    T.

  14. Sara Rao
    Sara Rao says:

    Here is my css code for the menu
    The menu is menuh and has a container menuh-container

    Right below the drop-down menu is a banner that will be displayed horizontally across the page.
    When I hover over the menu items, the drop down is displayed but it gets hidden behind the banner image and I am unable to click on the drop-down links.
    This ONLY happens in IE6
    #menuh-container
    {
    position: absolute;
    top: 2.5em;
    left: 48%;
    width:660px;
    margin-left:-280px;

    }

    #menuh
    {
    font-size: small;
    font-family: arial, helvetica, sans-serif;
    width:100%;
    float:left;
    margin:0.1em;
    margin-top: 1em;
    }

    #menuh a
    {
    text-align:left;
    display:block;
    border:none;
    white-space:nowrap;
    margin:0;
    padding: 0.1em;
    }

    #menuh a:link, #menuh a:visited, #menuh a:active /* menu at rest */
    {
    color: white;
    background-color: #0c2e6c;
    text-decoration:none;
    }

    #menuh a:hover /* menu at mouse-over */
    {
    color: white;
    background-color: #663399;
    text-decoration:none;
    }

    #menuh a.top_parent, #menuh a.top_parent:hover /* attaches down-arrow to all top-parents */
    {
    background-image: url(navdown_white.gif);
    background-position: right center;
    background-repeat: no-repeat;
    text-align:center;
    }

    #menuh a.top_parent:hover
    {
    background-color: #009900;
    }

    #menuh a.parent, #menuh a.parent:hover /* attaches side-arrow to all parents */
    {
    background-image: url(nav_white.gif);
    background-position: right center;
    background-repeat: no-repeat;
    text-align:left;
    }

    #menuh ul
    {
    list-style:none;
    margin:0;
    padding:0;
    float:left;
    width: 7em; /* width of all menu boxes */
    }

    #menuh li
    {
    position:relative;
    min-height: 1px; /* Sophie Dennis contribution for IE7 */
    vertical-align: bottom; /* Sophie Dennis contribution for IE7 */
    }

    #menuh ul ul
    {
    position:absolute;
    z-index:1;
    top:auto;
    display:none;
    padding: 1em;
    margin:-1em 0 0 -1em;
    width: 23em;

    }

    #menuh ul ul ul
    {
    top:0;
    left:100%;
    }

    div#menuh li:hover
    {
    cursor:pointer;
    z-index:1;
    }

    div#menuh li:hover ul ul,
    div#menuh li li:hover ul ul,
    div#menuh li li li:hover ul ul,
    div#menuh li li li li:hover ul ul
    {display:none;
    z-index:1;}

    div#menuh li:hover ul,
    div#menuh li li:hover ul,
    div#menuh li li li:hover ul,
    div#menuh li li li li:hover ul
    {display:block;
    z-index:1;}

    /* End CSS Drop Down Menu */

    #banner
    {
    margin-bottom:37px;
    }

    Please help
    Thanks!

  15. Kriesi
    Kriesi says:

    hey Sara, I am afraid I guess no one here will have enough time to completely reproduce this IE 6 bug. it would be much easier if you provide a link to the website. If you dont have webspace just get an account at a free hosting service =)

  16. Sara Rao
    Sara Rao says:

    I have tried working with z-indices. either they are not the problem or I do not know how to effectively use them.

  17. some_dewd
    some_dewd says:

    This tutorial was great and much more step by step than the other drop down tuts. Everything is working okay for me in Opera and Firefox, but in IE the menus are dropping down too far too the right. I linked my site for reference, I’d appreciate any help or tips.

  18. Tomas
    Tomas says:

    @Sara: the is a known problem with element overlay in IE 6.0 – we use transparent iframe to “flatten” and hide all those form element under navigation or .

  19. biotech
    biotech says:

    Hi Kriesi,
    Very nice tutorial…that much so that I am trying to implement a version of it and have a little trouble doing it.
    It seems that I can not successfully wrap “child” lists items when I hoover over them….child links (list items) that whose content is in 2 lines as the list list is with set width (this is desired effect).
    Any suggestions?

    temp link: http://mostarnetworks.com/biotech/dmenut.html

Trackbacks & Pingbacks

  1. [...] CSS Menu with a dash of jQuery This is another simple mouseover drop down menu, but this time it’s kicked up a notch with a [...]

  2. [...] Multilevel Dropdown Menu with CSS &amp Supercharge it with jQuery [...]

  3. [...] 31. ??css?jQuery???????? [...]

  4. [...] Create a multilevel Dropdown menu with CSS and improve it via jQuery [...]

  5. [...] 4- Create a multilevel Dropdown menu with CSS and improve it via jQuery [...]

  6. [...] Create a Multilevel Dropdown Menu with CSS and Improve it via jQuery Create a Multilevel Dropdown Menu with CSS and Improve it via jQuery [...]

  7. [...] 20. Create a multilevel Dropdown menu with CSS and improve it via jQuery [...]

  8. [...] 5. Create a multilevel Dropdown menu with CSS and improve it via jQuery [...]

  9. [...] Create a multilevel Dropdown menu with CSS and improve it via jQuery [...]

  10. [...] 5. Create a multilevel Dropdown menu with CSS and improve it via jQuery [...]

  11. [...] Create a multilevel Dropdown menu with CSS and improve it via jQuery [...]

  12. [...] How to Make a Smooth Animated Menu with jQuery Designing the Digg Header: How To & Download Create a multilevel Dropdown menu with CSS and improve it via jQuery ?????8 Layout Solutions To Improve Your [...]

  13. [...] HTML, CSS ve JQuery kütüphanesi kullan?c?larak JavaScript ile yap?lm?? bu menüyü çal??malar?n?zda diledi?iniz ?ekilde kullanabilirsiniz. Resimden görüntüsünü görebilir, bu ba?lant?dan da kaynak kodlar? indirebilirsiniz. [...]

  14. [...] Create a multilevel Dropdown menu with CSS and improve it via jQuery [...]

  15. [...] had jquery dropdown menu from http://www.kriesi.at and jquery.innerfade.js on a home page. The dropdown menu goes behind the slideshow in IE 6/7.I [...]

  16. [...] Tutorial para criar um belo menu com sub-itens (demonstração online) Plugin para cortar fotos com jQuery. [...]

  17. [...] 31. ??css?jQuery???????? [...]

  18. [...] 20. Create a multilevel Dropdown menu with CSS and improve it via jQuery [...]

  19. [...] Tutorial para criar um belo menu com sub-itens (demonstração online) Plugin para cortar fotos com jQuery. [...]

  20. [...] 8?Sexy Drop Down Menu w/ jQuery & CSS9?jQuery & CSS Example – Dropdown Menu10?Create a multilevel Dropdown menu with CSS and improve it via jQuery 11?jQuery Simple Drop Down Menu12?jQuery (mb)Menu [...]

  21. [...] CSS Menu with a dash of jQuery This is another simple mouseover drop down menu, but this time it’s kicked up a notch with a [...]

  22. [...] 17. CSS Menu with a dash of jQuery [...]

  23. [...] Create a multilevel Dropdown menu with CSS and jQuery [...]

  24. [...] + JavaScript for jQuery) to implement a versatile multilevel navigation menu (please send me the original source if you find it!). I think it’s the simpler and faster way to do that. The result is something [...]

  25. [...] Create a multilevel Dropdown menu with CSS and improve it via jQuery [...]

  26. [...] 17. CSS Menu with a dash of jQuery [...]

« Older CommentsNewer Comments »

Comments are closed.