Easy CSS Dropdown Menus

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.

15 Responses

  1. On May 12, 2008
    3:35PM

    Niki Brown said:

    How is this better than the tried and true suckerfish dropdowns or son of suckerfish dropdowns?

  2. On May 12, 2008
    3:45PM

    Rob Glazebrook said:

    Hi Niki,

    There’s not really a huge difference between mine and the Suckerfish version — the JavaScript I’m recommending is from the Son of Suckerfish dropdowns page, even. But I decided to write about dropdowns for two reasons:

    1. I do things a little differently than they do (not better or worse… just different).
    2. I can explain it in a different way, which will hopefully make sense to different people.

    Just because someone has already done something similar elsewhere on the web doesn’t mean I can’t (or shouldn’t) offer my own perspective… otherwise, every CSS site out there could just link to the CSS spec sheet and tell people to figure it out themselves! :)

  3. On May 12, 2008
    10:14PM

    Abhisek said:

    ver nice and explains everything. i used to be scared of creating a drop down. now it seems too easy

  4. On May 12, 2008
    11:45PM

    David Hucklesby said:

    Nice.

    While you are at it (adding JScript for IE6 that is) you *could* go the whole hog and use Dean Edwards’s IE7 to make IE6 behave:

    http://dean.edwards.name/weblog/2008/01/ie7-2/

  5. On May 23, 2008
    2:56PM

    Damon said:

    Thanks for this. I like it, it has helped me already!!

    I was wondering… I know it’s possible to add one more level to this menu, but just can’t figure it out.

    Any insight?

    Thanks again.

  6. On July 01, 2008
    7:15AM

    John Law said:

    Many thanks for the drop-down navigation model which I’ve adapted for the update of my site. Having decided to use dropdowns I tried and failed with numerous ‘fishy’ variations until I discovered yours.
    Though I work on a mac, with VMWare I am able to run XP & was able to test in XP Firefox & of course the clunky Explorer which broke all the other variations I tried, probably due to my incompetent implementation.

    Please take a look at the site:

    john-law.org.uk/new-site

    any comments welcome before it goes live next week.

  7. On July 01, 2008
    7:44AM

    Rob said:

    That looks like a great implementation, John! Simple, lean, and attractive. I’m glad the tutorial was able to help.

  8. On July 10, 2008
    5:50AM

    Sonam said:

    Hi,
    Thanks a lot! , best dropdown I came across…clean and effective.
    Keep up the gud work dude.

  9. On July 29, 2008
    3:51PM

    jon said:

    I like this adaptation of the suckerfish menus, but am having trouble getting enough width for my li li elements.
    The background color is displaying wide enough, but the nested list links are breaking across a line return.
    A short version is on http://www.radford.edu/~jcharris/tmp1/dropdown.html
    You can see the difficulty under “Ways to Give” & “Leadership”.

    Any tips?

    Thanks!

  10. On July 29, 2008
    4:45PM

    Rob Glazebrook said:

    Hi Jon,

    The problem stems from this rule:

    #navbar li { width: 100px; }

    That rule is doing two things: it’s setting a defined width on your top-level navigation, which is what I think you’re going for. However, it’s also setting a defined width on the list items in your ~secondary~ nav, which isn’t what you’re going for.

    So to counteract that rule, you just need to write a more specific rule for subnavigation. This should work:

    #navbar li li { width: 150px; }

    That way, your subnav items should fill up the full 150px you’ve given them.

  11. punjabiace said:

    can u create a vertical sub drop nav bar

  12. Vince Mendella said:

    I have put this menu into a site that I am working on but have run into a large problem. The sub-menus seem to be off center. It seems when I roll over a heading in the menu the drop down sub-menu is off to the right hand side. I am not sure on how to fix this. If someone can take alook, I have the CSS in the head of the file.

    http://www.mts.net/~vmendell/indextest.html

    It works fine on Firefox and Safari, but when I test it on Explorer the menu is off center.

    Thank you,
    Vince Mendella

  1. On May 19, 2008
    9:37PM

    css menu styling said:

    [...] walk you through developing a clean, semantic dropdown menu using XHTML and css that works in all mhttp://www.cssnewbie.com/easy-css-dropdown-menus/Building Websites with Joomla! 1.5 SlashdotMichael J. Ross writes “Web developers are oftentimes [...]

  2. On May 29, 2008
    7:39AM

    Stewart Schatz » Blog Archive » CSS Dropdown Menus said:

    [...] CSSnewbie has article entitled "Easy CSS Dropdown Menus". [...]

  3. On August 19, 2008
    5:01PM

    5 Great Uses for the CSS Display Property - CSSnewbie said:

    [...] you can create beautiful dropdown menus without a single line of JavaScript. Want to learn how? Here’s a tutorial on creating easy CSS dropdown menus, and here’s a second tutorial on creating special horizontal [...]

Leave a Comment