Everywhere I look on the net I see copies of copies when it comes to CSS dropdown menus. They are messy, flawed, limited, illogical and require manually coding very deep nesting to achieve submenus. So if your tired of all this pishposh and want a menu with unlimited submenus with no extra code and that works on all modern browsers (including the not so modern IE7), read on.
Features
- Pure CSS
- No hacks
- No excessive styling so you can get on with it yourself
- Clean and short code
- No change to your normal <ul> <li> type of menu
- Unlimited submenus
- XHTML compliant
- Seperate styling for top level and sub menus
Demo
ok lets get started..
First you will need a unordered list which represents your menu, the only requirement is that the parent ul tag has an id of “nav”:
<ul id="nav"> <li><a href="#" class="active">Menu 1</a></li> <li><a href="#">Menu 2</a> <ul> <li><a href="#">Menu 2:1</a></li> <li><a href="#">Menu 2:2</a></li> <li><a href="#">Menu 2:3</a> <ul> <li><a href="#">Menu 2:3:1</a></li> <li><a href="#">Menu 2:3:2</a> <ul> <li><a href="#">Menu 2:3:2:1</a></li> <li><a href="#">Menu 2:3:2:2</a></li> </ul> </li> <li><a href="#">Menu 2:3:3</a></li> <li><a href="#">Menu 2:3:4</a></li> </ul> </li> </ul> </li> <li><a href="#">Menu 3</a></a> <ul> <li><a href="#">Menu 3:1</a></li> <li><a href="#">Menu 3:2</a></li> </ul> </li> </ul>
Next it is time to set up the CSS, unlike other menus we work our way down the list while avoiding things like #nav li li:hover li ul li ul { something } and accidental inheritance like so many others seem keen on doing.
/* position the menu */
#nav { float:left; z-index:100; position:relative; }
/* first level */
#nav > li { float:left; list-style:none; font:20px "Arial Narrow", Arial, sans-serif; }
#nav > li > a { display:block; line-height:38px; padding:0 18px; color:#363636; text-decoration:none; }
#nav > li > a:hover, #nav > li .active { background-color:#363636; color:#fff; }
/* template for all lists below first */
#nav > li ul { display:none; position:absolute; float:left; width:10em; background-color:#666; }
#nav > li ul a { display:block; width:10em; color:#fff; padding:8px 20px; font-size:16px; text-decoration:none; }
#nav > li ul a:hover { background-color:#bc3030; color:#fff; }
/* second level */
#nav > li:hover > ul { display:block; }
/* all levels below second */
#nav > li li > ul { margin:-36px 0 0 10em; }
#nav > li li:hover > ul { display:block; }
It should be straight forward what is going on here, but a brief summary:
#1 Position the menu via the parent ul (#nav { })
#2 Style the top level menu
#3 Create a template for all lists below this – the ul is absolutely positioned, the width is fixed to 10em, and display:none is default to hide the menus. The rest is styling.
#4 Create hover classes for 1) the menu directly below the first (it doesnt need to be offseted), and then offset every menu below that by the menu width (10em) and the height of an item so it lines up (-36px)
Fixing 2 IE7 bugs
It should come as no suprise that IE7 doesn’t behave properly. The first bug we must quash is a positioning fault. Make a IE conditional in your header like so:
<!--[if IE 7]>
<style type="text/css">
#nav > li li:hover > ul { left:10em; margin:0; }
</style>
<![endif]-->
Ah stop right there! Problems still you say? The next bug we must fix is the IE7 ghosting bug that has to do with hasLayout stuff (look it up if you want), you will notice the next time you go to the top level menu item and hover you will see the background of all the nested menus in the layout they were last in. We fix this by editting the above conditional to the following:
<!--[if IE 7]>
<style type="text/css">
#nav > li li:hover > ul { left:10em; margin:0; margin-top:-38px; }
#nav li:hover {position:relative; z-index:1;}
</style>
<![endif]-->
And that’s it for the CSS! Next you may consider augmenting your menu with JQuery for animations (see easing plugin), hover delay (see hoverIntent plugin), and nice behaviour on the top level (see lavalamp plugin).
Another common IE7 problem not to do with the menu (z-index bug): the menu drops down over a div below but the menu sinks beneath content there and menu hovering is broken
- To fix this problem the container the menu is inside needs to have a manually specified higher z-index than the container below.