Easy CSS Dropdown Menus

Published May 12, 2008 by Rob Glazebrook.

Attractive dropdown menus have long been the realm of Flash developers and advanced JavaScript gurus. But that needn’t be the case. This tutorial will walk you through developing a clean, semantic dropdown menu using XHTML and CSS that works in all modern browsers!

Let’s start with the XHTML first and foremost. It’s surprisingly simple:

<ul id="navbar">
	<li><a href="#">Item One</a><ul>
		<li><a href="#">Subitem One</a></li>
		<li><a href="#">Second Subitem</a></li>
		<li><a href="#">Numero Tres</a></li></ul>
	</li>
	<!-- ... and so on ... -->
</ul>

As you can see, our navigation bar consists of nested unordered lists and anchor tags. The key to this working correctly is to properly nest your unordered lists, wrapping the list item around the unordered list that is nested under it (for more on that topic, see this article on styling nested lists). The main list items will be our main navigation bar, while the nested unordered lists will become our subnavigation elements. The navigation bar also works without submenus, so you can mix and match as needs be. Also note that, other than an ID on our primary containing unordered list, there are no additional classes or IDs required!

Next, we’ll start adding a few styles to our navigation bar:

#navbar {
	margin: 0;
	padding: 0;
	height: 1em; }
#navbar li {
	list-style: none;
	float: left; }
#navbar li a {
	display: block;
	padding: 3px 8px;
	background-color: #5e8ce9;
	color: #fff;
	text-decoration: none; }

Here, I’ve removed the margin and padding from the main list, removed all list styling from all the list items, and floated the individual items left. I’ve also added a bit of styling to the anchors, just to make it look a little more like a navigation bar. As you can see, this really isn’t any different than making any other sort of navigation bar to start out with.

The only real oddity here is the “height: 1em;” rule on the navbar ID: this forces the navbar to have a specific height (1em) and width (100% by default), meaning I don’t have to do anything special to “clear” the navigation afterwards. Without that rule, I’d generally need to apply a “clear: left;” to whatever came immediately after the navigation to prevent it from trying to fill the space voided by those left-floated list items. The actual height is arbitrary: as long as a height is specified, the list will retain its block-level status.

Next, we can apply some styles to the subnavigation section:

#navbar li ul {
	display: none; 
	width: 10em; /* Width to help Opera out */
	background-color: #69f;}

This is pretty straightforward: we’re applying a display: none to prevent the submenu from displaying by default, and giving it a background color to make it stand out against the background. The only odd bit is the width property, which is mostly there to prevent Opera from doing some weird things with the width of the submenus (Opera makes them strangely small without a width specified). However, it also ads a nice bit of consistency to the submenus, so I don’t really mind the “fix.” I chose 10em because that allowed all of my submenu items to exist on one line, but you could choose whatever size works for you.

Now all we need to do is style the list for its “hover” state:

#navbar li:hover ul {
	display: block;
	position: absolute;
	margin: 0;
	padding: 0; }
#navbar li:hover li {
	float: none; }
#navbar li:hover li a {
	background-color: #69f;
	border-bottom: 1px solid #fff;
	color: #000; }
#navbar li li a:hover {
	background-color: #8db3ff; }

Let’s go through this bit by bit. The first rule causes the submenu to reappear when the user hovers over the containing list item (this is where the properly nested lists come in handy). We’re using position: absolute on the menus to ensure they don’t push any content below the navigation out of the way. The margin and padding are simply getting rid of the default spacing on the lists so we can style them ourselves.

Next up comes the “float: none” rule on the list items. This is just preventing the items in the submenu from floating left: it’s counteracting our previous “float: left” rule so that our submenu doesn’t mimic our main navigation elements.

The rules we’re applying to “#navbar li:hover li a” are purely stylistic: I’m applying a background color, bottom border, and changing the color of the anchor. You could set these to be anything you wanted whatsoever.

And finally, I’m applying a different background color to the anchor when it’s being hovered over, just to help set it apart from the other items in the list. This is to improve usability: the user can easily see which item their cursor is over.

That’s all it takes! You can see a working example here. I’ve tested this and found it working in Firefox 2, IE7, Opera 8.5+, and Safari for Windows. Of course, you’ll note that I’m leaving out the usual party pooper: Internet Explorer 6. Because of IE6’s limitations on :hover states (you can only hover over anchors in IE6, instead of any element like in all the other browsers), this fantastic little technique doesn’t work. Unless, of course, you’re willing to add in a couple of lines of JavaScript.

The brilliant hive mind that is Patrick Griffiths and Dan Webb have come up with a fantastic JavaScript solution for Internet Explorer that solves the :hover problem in just 12 lines of code. The version I’m using looks like this:

sfHover = function() {
	var sfEls = document.getElementById("navbar").getElementsByTagName("li");
	for (var i=0; i<sfEls.length; i++) {
		sfEls[i].onmouseover=function() {
			this.className+=" hover";
		}
		sfEls[i].onmouseout=function() {
			this.className=this.className.replace(new RegExp(" hover\\b"), "");
		}
	}
}
if (window.attachEvent) window.attachEvent("onload", sfHover);

The concept is pretty brilliantly simple (even if the code looks complex). The function goes through your document and finds every list item contained within the “navbar” id (you could change this to be whatever you wanted). It then applies a “mouseover” and “mouseout” state on every item: it adds a class of “hover” to the list item whenever it’s being moused over, and removes it when the cursor wanders off. The result is you can then apply your CSS to the .hover class as well as the :hover pseudo-class and create identical results across the board. So all you need to do is modify your CSS like so:

#navbar li:hover ul, #navbar li.hover ul {
	display: block;
	position: absolute;
	margin: 0;
	padding: 0; }
#navbar li:hover li, #navbar li.hover li {
	float: none; }
#navbar li:hover li a, #navbar li.hover li a {
	background-color: #69f;
	border-bottom: 1px solid #fff;
	color: #000; }

And that’s it! With just three tiny changes to your CSS and a dozen lines of JavaScript, you have a CSS dropdown solution that works on every single modern browser – even the ones that aren’t exactly standards-compliant.

237 Responses

  1. gavin petgrave (reply)

    ok first and foremost i must commend u on an awesome build and walk through. like this stuff was so on point a monkey could follow it. and i thank u for that. everything works great but being me i made some alteration. i keep having a bit of a problem. the website i’m working on requires that i align the menu bar to the right. it worked but wen i go over the last drop down the subitem are off screen. no water wat i try it just won’t move. do u have any idea as to why it’s like that. if u do it would be greatly appreciated.

  2. Paul (reply)

    I am trying to replace my current navigation with a hover over drop down. I have created a copy page to work on, not published, the page is the top banner. I have put as suggested however all I get on the page is the bullet pointed menues. To me it seems that the page coding is not connecting with the CSS to provide the effects. Can you advise why this may be what I am missing to point the coding to this css text?

  3. Joanne (reply)

    Thank you for this! Such a simple thing and so darn hard to figure out without help! I’m a volunteer, so there’s no one to ask. Thanks again for sharing your insights.

  4. Adrian (reply)

    I’ve tried many tutorials to do this, and this one is by far the best! for people asking how to add another level of sub menus, try this that i managed to get working. Add to the end of your CSS:

    #navbar li li ul {
    visibility: hidden;
    }
    #navbar li li:hover ul {
    visibility: visible;
    display: block;
    left: ;
    margin-top: -1.5em;
    }
    This will allow you to add infinate levels of sub menus in theory, but only for the width of your page!

  5. Renard (reply)

    Please i wish to know if there is any possibility that i can apply such a drop down, just to one topic. That is, i have a topic in my block that i want it to drop down when hovered but not in the menu. so if there is any possibility then i ask of help. thanks.

  6. Claudia (reply)

    Hello,
    I tried this, but for some reason the ul isn’t showing up upon hover.

    .menu ul ul
    {

    display:none;

    }

    .menu ul li:hover ul
    {
    display: block;
    position: absolute;
    margin: 0;
    padding: 0;
    }

    .menu li:hover li
    {
    float:none;
    }

    .menu li:hover li a {
    background-color: #69f;
    border-bottom: 1px solid #fff;
    color: #000; }

    .menu li li a:hover {
    background-color: #8db3ff;
    }

    .menu ul a
    {color:white;
    text-decoration:none;
    }

    .menu ul li:hover
    {background:#0f5baa;
    cursor:pointer;
    -moz-box-shadow: inset 0 0 5px 5px #006699;
    -webkit-box-shadow: inset 0 0 5px 5px #006699;
    box-shadow: inset 0 0 5px 5px #006699;
    }

  7. George (reply)

    Had problem with disappearing menus when the hover hit the border of the links. Fixed by adding z-index:9999; to the #navbar li ul {} rule.

    #navbar lu ul {
    display: none;
    width: 10em;
    background-color: #69;
    z-index: 9999;
    }

  8. pietro dellacqua (reply)

    with jQuery is easier:
    $(jQuery).ready{
    var sfEls = document.getElementById(“navbar”).getElementsByTagName(“li”);
    for (var i=0; i<sfEls.length; i++) {
    sfEls[i].onmouseover=function() {
    this.className+=" hover";
    }
    sfEls[i].onmouseout=function() {
    this.className=this.className.replace(new RegExp(" over\\b"), "");
    }
    }
    });

  9. Elangovan (reply)

    Hi Rob,

    Excellent info. I am using this menu in our app. I tried to add the second sub level to slide to right. ir works but displays 1 level down to right. COuld you please help me?

    This is what i added for second level.

    #navbar li:hover ul li:hover ul, #navbar li.hover ul li.hover ul
    {
    display: block;
    position: absolute;
    left: 100%;
    top:auto;
    }

    #navbar li:hover ul ul, #nav li.sfhover ul ul {
    left: -999em;
    }

    #navbar li:hover ul, #navbar li li:hover ul, #navbar li.sfhover ul, #navbar li li.sfhover ul {
    left: auto;
    }

  10. Obaid (reply)

    Ok, HTML tags cannot be directly posted here. So, I am moderating the question.
    Thanks for your article. I am facing some problem with this drop down menu thing. I read your article and it helped me. In the beginning, the drop down worked but when I added some more styling then it stopped working. Other divs and navigation bar is working fine but the drop down menu is not working. Can you please help me pointing out what should be corrected here?

    The parent div is nav-bar-left and the style is
    .nav-bar-left {
    float; left;
    overflow: hidden;
    width: 980px;
    height: 26px;
    background-color: Lavender;
    border: 1px solid MidnightBlue;
    border-radius: 3px;
    -moz-border-radius: 3px;
    -webkit-border-radius: 3px;
    }
    The navigation div is #horizontalmenu which resides within the above parent div and the style is
    #horizontalmenu {
    width: 733px;
    margin: 0;
    position: relative;
    float: left;
    padding: 0;
    }
    Rest of the styling for navigation bar is
    #navbar {
    list-style-type: none;
    margin: 0;
    width: 100%;
    padding: 0;
    position: relative;
    display: inline-table;
    height: 26px;
    z-index: 5;
    }
    #navbar li {
    float: left;
    position: relative;
    }
    #navbar a:link, #navbar a:visited {
    display: block;
    color: #333;
    background-color: lavender;
    text-align: center;
    padding: 6px 10px;
    border-style: solid;
    border-color: MidnightBlue;
    border-width: 0 1px 0 0;
    text-decoration: none;
    font-size: 14px;
    line-height: 14px;
    }
    #navbar a:hover, #navbar a:active {
    color: #fff;
    background-color: #6b0c36;
    text-decoration: underline;
    }
    #navbar ul {
    left:-9999px;
    list-style-type: none;
    margin: 0;
    padding: 0;
    position: absolute;
    }
    #navbar ul li {
    float:none;
    border-style: solid;
    border-color: Lavender;
    border-width: 0 1px 1px 1px;
    }
    #navbar ul a {
    white-space: nowrap;
    }
    #navbar li:hover ul {
    left: 0;
    }
    #navbar:hover a {
    text-decoration: none;
    }
    #navbar li:hover ul a {
    text-decoration: none;
    background-color: Lavender;
    color: #333;
    }
    #navbar li:hover ul li a:hover {
    background-color: Lavender;
    color: #333;
    }
    So, why is it not working and what can be done?

  11. french chairs (reply)

    I am not sure where you are getting your information, but great topic.
    I needs to spend some time learning much more or understanding more.
    Thanks for excellent information I was looking for this information for my mission.

  12. sheena (reply)

    wow! amazing! i already found the site that i’ve been looking for. thank you for ur excellent information. it is easy to follow and understand all the stuffs u are doing. thank u again. :))))))

  13. Callie (reply)

    Hello there! I know this is somewhat off topic but I was wondering which blog platform are you using
    for this website? I’m getting fed up of WordPress because I’ve had issues with hackers
    and I’m looking at alternatives for another platform.
    I would be awesome if you could point me in the
    direction of a good platform.

    Also visit my web site; good quality paint,
    Callie,

  14. http://www.youtube.com (reply)

    With havin so much content do you ever run into any problems of plagorism
    or copyright violation? My website has a lot of exclusive
    content I’ve either authored myself or outsourced but it seems a
    lot of it is popping it up all over the web without my permission. Do you know any
    ways to help reduce content from being stolen? I’d definitely appreciate it.

    Feel free to visit my weblog; 7 zip free download 7-zip
    rar (http://www.youtube.com)

  15. Janice (reply)

    Wow that was strange. I just wrote an incredibly long comment but after I clicked submit my comment didn’t show up.
    Grrrr… well I’m not writing all that over
    again. Anyways, just wanted to say superb blog!

    Take a look at my homepage

  16. Maria (reply)

    Hello i am kavin, its my first occasion to commenting anywhere, when
    i read this paragraph i thought i could also make comment due to this brilliant article.

    Here is my page :: non-workout workout (Maria)

  17. Maryjo (reply)

    That is a great tip particularly to those new to the blogosphere.
    Simple but very accurate information… Thank you for sharing this one.
    A must read post!

    My blog post :: basement solutions – Maryjo,

Leave a Reply

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

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>