Create your own drop down menu with nested submenus using CSS and a little JavaScript

Drop down menus are among the coolest things on the web. Beside that they are also very good for creating navigations that contain many elements. The main problems of creating drop down menus lies in the Internet Explorer’s inappropriate way of displaying :hover pseudo class (not recognized anywhere except in A tag), and the problem in calculating the z-index when an element is positioned absolutely inside a relatively positioned element.

Drop down menu example 1

How to start?

To better understand this article, you could read the Suckerfish dropdown article on A List Apart. You did already? Then please proceed. We will create a horizontal menu with a submenu on “Projects” and two submenus (Older projects and Active projects). Here is the code.


<ul id="menu">
	<li><a href="#" title="Homepage" class="selected">Homepage</a></li>
	<li><a href="#" title="About us">About us</a></li>
	<li><a href="#" title="Projects">Projects</a>
		<ul>
			<li><a href="#" title="Older projects">Older projects</a>
				<ul>
					<li><a href="#" title="2003 projects">2003 projects</a></li>
					<li><a href="#" title="2004 projects">2004 projects</a></li>
					<li><a href="#" title="2005 projects">2005 projects</a></li>
				</ul>
			</li>
			<li><a href="#" title="Active projects">Active projects</a>
				<ul>
					<li><a href="#" title="Leaf industries">Leaf industries</a></li>
					<li><a href="#" title="Omnicron">Omnicron</a></li>
					<li><a href="#" title="Unkown.com">Unkown.com</a></li>
				</ul>
			</li>
		</ul>
	</li>
	<li><a href="#" title="Contact">Contact</a></li>
</ul>

Let’s style up the elements, shall we? We will remove the bullets and remove any padding and margin we could have from the first level and second level menu (#menu, #menu ul { ... }). Now we will style up first level list items. We will use a float method and we will position the elements relatively. To create a “please click here on this button look” we will use line-height: 4.5em; (to create a 45px high button) and width: 10em;.


#menu, #menu ul {
	list-style:none;
	padding:0;
	margin:0;
}
#menu li {
	float:left;
	position:relative;
	line-height: 4.5em;
	width: 10em;
}

Now, we are going to position the second level navigation lists. As we said, we will position them absolutely inside their list items. Because the first level list has the ID #menu if we want to define a style for the next level we must use #menu li ul which actually says: “This refers to all the second level UL’s inside first levelLI’s, inside UL’s with ID #menu” (we could also define it with ul#menu li ul, but this way we keep it a bit shorther).

Okay, now this is a tricky part which had me thinking a lot. We could position the second level navigation with top and left, and this will work just fine in Firefox. Because of IE and its problem of calculating the correct z-index position, we will use the margin-top and margin-left instead (I will show you why is this very important later in this article). So we will position the second level navigation a little above the first level (as shown in the picture before), and the third level little above the second level (it looks just cool to me :).

We don’t want to show the second level navigation until the mouse goes over the navigation element that has the submenu (Projects). So we should use the display:none; here. Because we cannot see the second level, there is no chance we will see the third, right?


#menu li ul {
	position:absolute;
	margin-top:-1em;
	margin-left:.5em;
	display:none;
}
#menu ul li ul {
	margin-top:-3em;
	margin-left:7em;
}

Styling the links

For start, we have to display links as blocks so the whole button area would become a link. With #menu a { ... } we will define the linked elements. Border-right is here to move apart the first level menu elements. Why don’t use the margin-right:1px; instead, you ask? Well, there is no exact reason. If we use margins to move apart the elements, we won’t see the problem until we would like to remove the elements in the second and every next level of navigation. If we use margin-bottom:1px; to remove these elements, the elements will disappear because we would have a gap between them. While using borders instead of margin, the gap isn’t there, because the border is the part of an element.

Our menu elements will have redefined background, color, text-decoration and padding. Because we used line-height to define height of the buttons, we do not have to define top and bottom padding, but we have to define it for left and right.

We will set up slightly different styles for the first and second navigation levels to make a difference between them. To the second level menu elements we will add right, left and bottom borders, and a border to the <ul> tag. This way we don’t have to overlap the elements by 1px on top.


#menu a {
	display:block;
	border-right:1px solid #fff;
	background:#E0F574;
	color:#3B3B3B;
	text-decoration:none;
	padding:0 10px;
}
#menu a:hover {
	background-color:#5798B4;
	color:#fff;
}
#menu ul {
	border-top:1px solid #fff;
}
#menu ul a {
	border-right:none;
	border-right:1px solid #fff;
	border-bottom:1px solid #fff;
	border-left:1px solid #fff;
	background:#AEC245;
}

The second level isn’t visible by default, because we hided it with display:none;. Now, we want to display a second level, but not the third. If we simply add #menu li:hover ul { display:block; } we would display both the second and the third level because this is inherited. So, we will add #menu li:hover ul ul { display:none; } to hide the third level. If you wish to add a fourth level of navigation, you can simply do that by adding one more UL to the CSS code.


/* SHOW SUBMENU 1 */
#menu li:hover ul, #menu li.over ul {
	display:block;
}
#menu li:hover ul ul, #menu li.over ul ul {
	display:none;
}
/* SHOW SUBMENU 2 */
#menu ul li:hover ul, #menu ul li.over ul {
	display:block;
}

JavaScript to the rescue

You surely noticed the second selector #menu li.over ul and wondering why is this here. If you have read the ALA article about drop down menus you already know that. As you can remember, Internet Explorer doesn’t recognize the :hover pseudo class anywhere but on the links. As we added a :hover pseudo class to the list item with li:hover, IE won’t display the submenus. We can use a JavaScript from A List Apart as this will work – but for displaying the second level only.

Thanks to my friend Miha Hribar, we now have the solution to that problem. Miha resolved the problem by writing a nice snippet of JavaScript code that resolves the problem of not displaying the third level submenu and so on. Here is what Miha says:

“The js snippet is very simple in its self. It finds the navigation by using the document.getElementById and the loops though all the child li tags (perhaps child tags is not the best analogy here, as the loop goes through all the li tags under the current ul node, wich in fact means all decendants of the parent). For every li tag it searches for a child ul tag, wich means that the list element has a sublist (or in our case a submenu). So when and if we find it, all we need to do is append a javascript function to the onmouseover and onmouseout events, wich just sets the inline style of the sublist to block when hovered over and none (thus hidding it from plain sight) when the mouse leaves the hover area.” Thanks again Miha!

Z-index and height problem in Internet Explorer

Earlier I mentioned the z-index problem in Internet Explorer. The problem appears when you want to position an element absolutely inside a relatively positioned element (our first level menu elements are floated left and positioned relatively, and our second and third levels unordered lists are positioned absolutely). Take a look at our next picture.

Drop down menu example 2

To prevent that from happening, first we positioned elements with margins instead of top and left positioning. The next thing we would do is use the conditional comments for IE. Because the problem pops up in the list-items elements, we will use static instead of relative position. The problem is solved.


<!--[if IE]>
<style type="text/css">
	#menu li {
		position:static;
	}
</style>
<![endif]--> 

To resolve the height calculation problem in IE we have to use this piece of code in our CSS file.


/* Fix IE. Hide from IE Mac \*/
* html ul li { float: left; height: 1%; }
* html ul li a { height: 1%; }
/* End */

The example

Take a look at our example

Further reading

z-index attribute at MSDN
Effect of z-index value to relatively positioned and absolutely positioned blocks
IE’s relative z-index calculation


Comments

39 responses to “Create your own drop down menu with nested submenus using CSS and a little JavaScript”

  1. Well done, the final result is very nice.

  2. woo… it is beauty.

  3. @Marko: Thanks for the comment. I love your site (tough I am way to slow on commenting this post, shame on me :)
    @Travis: Tnx.

  4. This is really good and I understand most if it, but when I’ve tried it in IE I get any text on the background bleeding through the menus. I’ve seen this before and I wonder if you can tell me why it happens?

  5. Simon, I must admit that I don’t understand the very nature of your problem. Could you spare me a link to it? Did you put the conditional comment for IE as I suggested in the end of the article?

  6. I have worked out my problem. I needed to set the z-index on the page container to lower than the menu’s, otherwise the text in the container was visible though the menu! In other words, my mistake!

    Now I am trying to change the menu so the second level is also shown as a drop down (rather than horizontal) menu.

    Keep up the great work…

  7. I like the final result very much but I am afraid that the javascript part doesn’t work in Safari.
    Many thanks anyway, keep it up, if find a solution I will let you know.

  8. Simon: I am glad this worked out for you.
    Thys: I didn’t have the opportunity to test the JS in Safari. If you or anyone else should find a solution to the problem please let us all know :)
    P.S.
    I like your site and your motion-graphics work you’ve done!

  9. Splendid display of code work…

    Happy validating with what you’ve done here, I’m a new pupil to the world of HTML & CSS. This site helped me get through an all nighter, thanks.

    MrFlick

  10. morganusvitus Avatar
    morganusvitus

    The site looks great ! Thanks for all your help ( past, present and future !)

  11. Thanks morganusvitus, I will try to write more interesting articles.

  12. Alfredo Avatar
    Alfredo

    This helped me a lot.

    One problem tho.

    I’m using images as each button in the drop-down menu, and i get gaps of about 3 pixels in between the buttons. i tried setting the font to 2pt and lineheight to .4, which works for other divs when i have gaps, but no luck. how do i avoid the gaps?

  13. Hi Alfredo. Could you give us the link to your problem?

  14. Alfredo Avatar
    Alfredo

    here is a section of the code with one button, as i don’t have any of it online yet. I’m pretty new to coding, if you find any other errors please do tell! :)

    Also note that i’m using safari, that’s where i get the gaps. i’ve also tried giving the style a very small font size and line height, which has worked for arranging div’s, but it doesn’t work for this.

    Also, i used a lot of reference to suckerfish drop-down menus, for compatibility, and copied the javascript hack for IE.

    Thanks in advance! love your site!

    body {
    }

    #nav, #nav ul {
    padding: 0;
    margin: 0;
    list-style: none;
    display: block;
    height: 27px;
    width: 157px;
    float: left;
    }

    #nav li ul {
    position: absolute;
    height: 27 px;
    width: 157px;
    left: -999em;
    }

    #nav li:hover ul, #nav li.sfhover ul { /* lists nested under hovered list items */
    left: auto;
    }









  15. Hi I am able to implement. I got correct display in IE 6.0. But In IE 7.0, firefox, opera its diplaying horizontally. Can you please suggest me to fix this problem. Thanks!

  16. Sree, could you give us a link to it, and a screenshot maybe? You could send it to e-mail (look the about page for it :).

  17. Thanks for the response… Please send me a test mail to . I haven’t find the mail id of you. Once again thanks for response and i hope my problem is going to be slove in soon with your help.

  18. I don’t know if I am missing something, but where might I find the javascript snippet that Miha wrote?

  19. Nevermind, I found it on someone else’s blog, from 2004…

    CSS Drive

  20. Anthony, the script can be found here. It is called within the example, and it works really fine :)

  21. Sree, I was having the same trouble with the menu in IE 7. After adding this bit of code, the problem was resolved:

    #menu li ul li {
    clear:left;
    }

    I hope that helps. Thanks for the excellent website, emanuel.

  22. Thank you very much Emanuel and thanks to Miha Hribar too.

    I´ve been really obsessed by this question in two last days.
    I wanted to make a third level in a dropdown menu for IE-Mac from the suckerfish article, but it was imposible.
    I´m very glad to find your page. The javascript code works perfectly and easily.

    Now there is a problem in Safari 2.0. It isn´t recognized the styles for the a:hover (in submenu and subsubmenu) items. Do you know any solution about this?

    Anyway, I want to end thanking you both again. Your discovery have been very important to me.

  23. Great CSS javascript tutorial,thanks for sharing you ideas.

  24. Christopher Avatar
    Christopher

    This drop-down menu is great.

    Can anyone offer any advice as to make it POP-up rather than drop down?

    Thanks!
    C.

  25. I m really very thankful to Emanuel and Miha Hribar.
    U both have solved my problem.
    it’s awesum…its too good…
    Thank you.

  26. great dropdown nav. The only issue that I have is lining up the subnav to meet the edges of the parent nav. any ideas??? Thanks

  27. This is the clearest and easiest way to do dropdowns that I’ve found…
    I’m trying to put in a third submenu though, and choking badly. You say “If you wish to add a fourth level of navigation, you can simply do that by adding one more UL to the CSS code.”

    Where? How? My head is swimming with ul’s and li’s!
    I’ve tried to work it out, but the two submenus are not quite enough for me to determine the correct pattern.

  28. Gaxton Avatar
    Gaxton

    Please are supposed to put all the codes in one file or we are supposed to place the syle in a CSS file.. pls help

    thank for the tutorial!

  29. I am having a problem with some of my second and third level menu items appearing behind the level above them in Firefox. Any suggestions?

  30. Robsco Avatar
    Robsco

    The menu bar is wonderful. I was wondering if you could shed some light on how I can center the menu bar on the page with a width of 900px without it creating two menu bars (one on top of the other)?

  31. Nice information, I really appreciate the way you presented.Thanks for sharing..

  32. It is excellent…Thanks for sharing with us.

  33. This is brilliant – thanks.

    David.

  34. I would like to thank you for this. I was looking for a way to create a menu that I felt looked as good as a flash based menu, but was very small in file size for optimum load times and this worked out perfectly for me.

    With very little editing I was able to do exactly what I wanted and it looked perfect for what I wanted. Thanks again for the code.

  35. Nice article ^_^

  36. Many thanks for this code, i am a into my second year of my degree have i have used this in my website design as part of my webdesign module.

  37. Hi Emanuel:

    You mentioned about problems with z-index and positioning of elements as shown on your last picture. When I added “z-index: 1;”, things went fine. Here is where I added it:

    #menu, #menu ul {
    list-style:none;
    padding:0;
    margin:0;
    z-index: 1;
    }

    on your drop-down-menu.css.

    I am tested it on Google Chrome (ver. 16.0.912.75 m) and IE 8.

    Without that line, both browsers are behaving similarly.

    Many many thanks to you and to Mihar.

  38. I came here to look for how to create submenus using CSS and that is exactly what I found. Thank you for sharing.

Leave a Reply

Your email address will not be published. Required fields are marked *