CSS Menus
Posted by Roger Keays, 16 April 2007, 10:07 AM
Building menus in HTML can dramatically improve the navigation and useability of your website. Getting it just right can be a very time consuming task though. There are a lot of javascript/dhtml implementations of menus out there but they almost all suffer from excessive bloat, inflexibility and complicated declarations to create the menu.
This blog describes how you can turn any existing HTML unordered list into a dropdown menu that is easily customisable through CSS. No need to call any fancy, indecipherable javascript, hardcode colors or sizes or fiddle with 3rd party library code: just import a single stylesheet and you're done.
We've set the following requirements for our menus:
- Require no additional markup (e.g. class names, id's, anchors) other than regular <ul> and <li> tags.
- Use no javascript.
- Use no browser-dependent code.
- Be independent of font size or screen size.
CSS menus aren't a new idea. Originally, I think credit for the idea goes to Eric Meyer [1]. Our method also uses ideas from howtocreate.co.uk [2] to support Microsoft Internet Explorer. Improvements made over these approaches include fixing flashing menu problems; cleaning up the css; supporting horizontal menus and fixing some problems displaying borders.
The Clean Implementation
Let's take the following markup, and convert it into a horizontal dropdown menu.
<ul>
<li>
<a href="#">Music</a>
<ul>
<li>
<a href="#">Classical</a>
<ul>
<li><a href="#">Mozart</a></li>
<li><a href="#">Rossini</a></li>
<li><a href="#">Pachelbel</a></li>
</ul>
</li>
<li>
<a href="#">Popular</a>
<ul>
<li><a href="#">70s</a></li>
<li><a href="#">80s</a></li>
<li><a href="#">90s</a></li>
</ul>
</li>
</ul>
</li> <!-- music -->
<li>
<a href="#">Dance</a>
<ul>
<li><a href="#">Salsa</a></li>
<li><a href="#">Cha Cha</a></li>
<li><a href="#">Waltz</a></li>
<li>
<a href="#">Swing</a>
<ul>
<li><a href="#">East Coast Swing</a></li>
<li><a href="#">Lindy Hop</a></li>
</ul>
</li>
</ul>
</li> <!-- dance -->
</ul> <!-- menu -->
Conceptually, we do the following steps with the CSS styling:
- Remove list style types
- Float the top-level <li> elements to arrange them horizontally
- Use CSS2 positioning to postion second and third-level <ul> elements
- Hide the second and third-level <ul> elements using display: none;
- Use a li:hover ul selector to display second and third-level <ul> elements when the mouse moves over their containing <li>
In detail, the styles we use are:
/* top level menu container */
ul {
list-style: none; /* no list bullets */
margin: 0px; /* don't try to indent lists */
padding: 0px; /* don't try to indent lists */
background: wheat;
}
/* top level menu items */
li {
position: relative; /* makes this a containing block */
float: left; /* align menu horizontally */
width: 5em; /* make each item the same width */
}
/* second level menu container */
ul ul {
border: 1px solid black;
display: none; /* don't show this menu by default */
position: absolute; /* use absolute positioning for submenu */
top: 100%; /* display directly under menu bar */
}
/* second level menu items */
li li {
float: none; /* makes this list a vertical one */
width: 8em; /* our second level menus are wider */
}
/* position third level menu container */
ul ul ul { top: 0px; left: 100%; }
/* make the anchor fill the li */
li a { display: block; padding: 3px; }
/* highlight effect on hover */
li a:hover { background: yellow; }
/*
* The magic which shows the menus. The > selector selects only an
* immediate child. So this selector says 'The ul directly below the
* li being hovered over'.
*/
li:hover > ul { display: block; }
/* clear the floated elements */
ul:after {
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
Internet Explorer
There are, as you might expect, several problems getting this to work on Microsoft Internet Explorer. The biggest of these is that IE 6 and 5.x don't support the :hover psuedoclass for <li> elements (quite ironic, seeing as it was Microsoft who invented the :hover psuedoclass, first using it for the <a> element). IE7 also has problems implementing this selector, particularly when the user changes the font size.
Unfortunately, we must look to javascript for a solution here. We can take advantage of IE's proprietary behavior CSS property to make this as transparent as possible:
li { behavior: url('IEmenus.htc'); }
With this file containing the javascript required to mimic the required :hover functionality:
<attach event="onmouseover" handler="rollOver" />
<attach event="onmouseout" handler="rollOff" />
<script type="text/javascript">
function rollOver() {
/* fix style */
element.className += ' hover';
/* prevent redraw of entire menu */
window.event.cancelBubble = true;
/* change display of child */
for (var x = 0; element.childNodes[x]; x++) {
if (element.childNodes[x].tagName == 'UL') {
element.childNodes[x].style.display = 'block';
/* force IE to draw the child properly */
element.childNodes[x].style.visibility = 'visible';
}
}
}
/*
* Called when the mouse moves off the li element.
*/
function rollOff() {
/* fix style */
element.className = element.className.replace(' hover', '');
/*
* Prevent redraw of entire menu by cancelling event bubble when moving
* onto children. Otherwise you get a lot of flickering in IE with large
* menus.
*/
var onto = window.event.toElement;
if (onto != null) {
do {
if (onto == element) {
window.event.cancelBubble = true;
return;
}
} while ((onto = onto.parentElement) != null);
}
/* change display of child */
for (var x = 0; element.childNodes[x]; x++) {
if (element.childNodes[x].tagName == 'UL') {
element.childNodes[x].style.display = 'none';
}
}
}
</script>
IE also has some typical rendering defects:
- Gaps are shown between the second and third-level menu items. This is fixed with li li { vertical-align: bottom; }.
- Anchor elements sometimes ignore the :hover event. This is fixed by putting the elements in hasLayout mode with li a:hover { zoom: 1; }.
- IE doesn't support the :after psuedoclass for clearing the floated <li>s. IE will clear elements when the containing block is in hasLayout mode though, so the fix is simply: ul { zoom: 1; }. You could also use width: 100%; here if that is appropriate for your design.
- For IE versions before 7, the position of the submenu is out by 1px when the containing <li> has an odd height in pixels. This is fixed with the following IE6-specific css expression:
ul ul { _margin-top: expression(this.parentNode.clientHeight % 2 == 0 ? 0 : 1); }
That's it. Take a look at the finished menu, which works in Firefox and Internet Explorer.
References
[1] http://meyerweb.com/eric/css/edge/menus/demo.html
[2] http://www.howtocreate.co.uk/tutorials/testMenu.html
| << Paging large data sets with a LazyList | Back to Blog | Ideas for JSF 2.0 >> |
Comment posted by: nelson on 17 April 2007, 4:46 PM
how do you get this to work for mac osx internet explorer??
Comment posted by: Roger Keays on 17 April 2007, 5:00 PM
Hi Nelson. This is untested on Mac/IE, Sorry.
Comment posted by: izzy on 31 August 2007, 5:47 PM
internet explorer is dead in mac. since there is no further development i dont think there is any point in trying to debug menu for IE mac
Comment posted by: ds on 19 October 2007, 10:52 AM
You may want to learn how to spell correctly before you try to publish anything.
dependant -->> dependent
independant -->> independent
Comment posted by: Roger Keays on 20 October 2007, 7:11 AM
tanx 4 da hedz up bout da reel bad spelling i fixd it up now i is a bad blogger
Comment posted by: dave on 16 December 2007, 7:30 PM
When copy the code which works in internet explore and save to test it on my pc the menu doesnot work.
So what do i need to do
Comment posted by: Roger Keays on 17 January 2008, 12:00 PM
Did you copy the .htc file too? IE is fussy about the url you use to reference the .htc file. If a relative one doesn't work, try an absolute one. It must also be served from the same domain as the .html file.
Comment posted by: jsfgeeks on 10 September 2009, 4:26 PM
Hi, you have done good job by this blog,its very helpful thanks
Comment posted by: Roger Keays on 10 September 2009, 10:14 PM
thanks jsfgeeks :)
Add a comment
Please visit http://www.ilikespam.com/blog/css-menus to add your comments.