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

Emanuel Blagonić

A Designer and Developer in one. With years of experience in design and front-end development, nowadays he is more of a project manager than a designer. Specialized in responsive design and UI design for touch–screen interfaces, Emanuel is also strongly connected with WordPress.

blog comments powered by Disqus