CSS menus with various levels of submenus
Author: Nediam <javier@nediam.com.mx>
Publication date: 2004-10-03
Last updated: 2007-03-11
Note 2007-03-11: New version of CSS menus. Some people have asked me that the CSS menus I explain in this tutorial also work in Internet Explorer and other browsers. I design 3 versions of CSS menus that will work in most of the browsers. The first version contains only one submenu level; the second version contains two leves of submenus and the last is the same as the second except that also contains Javascript code to detect the browser in order to assign an specific height to the menus. You can check and download them clicking
here,
here and
here.
In this tutorial I will explain how to create menus with submenus like
this,
or with various levels of submenus like
this one.
If you check the html source, you'll see that the main options of the menu are items from an unordered lists, and submenus, in turn, are unordered lists as well.
We will start by writing the html with no CSS rules. In line with the last example shown, the html would be
this, and will display
this. If you closely analyze the html code you will see that the whole menu is within a <ul> list, and that every main option is a list item <li>, which itself contains another <ul> list with the sub-options, which may have their own sub-options, and so forth. The trick is that the <ul> list that corresponds to a submenu must be within a list item <li>. In other words, the tags <ul> ... </ul> which correspond to a submenu must come before the tag that is closed by the list item <li> of the option:
<ul> <li><a href='#'>Option 3</a> <ul> <li><a href='#'>Suboption</a></li> <li><a href='#'>Suboption</a> <ul> <li><a href='#'>More</a></li> <li><a href='#'>More</a></li> </ul> </li> <li><a href='#'>Suboption</a></li> <li><a href='#'>Suboption</a></li> </ul> </li> </ul>
Continuing with the example, let's add these CSS rules:
<style type="text/css">
#menu
{
position: relative;
list-style-type: none;
}
#menu a
{
font-family: Arial, sans-serif;
font-size: 11pt;
color: #369;
display: block;
padding: 0.2em 0.5em;
text-align: center;
text-decoration: none;
}
#menu a:hover
{
background-color: #4487C2;
color: #FFF;
}
</style>
We are defining the id menu which we will assign to the main list that will include all the options and submenus. We also specify no bullets or other style (list-style-type:none) for the list items, and in the pseudo-class hover we will define a change of background and font color. It is also important to note that in the selector a we are specifying the display property (to know more about this property, check
this section of the CSS specification).
After adding this code to our example, the html code would look like
this, and would display
this.
It looks a bit better now, but the items on all lists are displayed vertically (which is the default), but we want to display horizontally. To do this we will define a class that we'll call menuitem and some ids where we will specify the absolute position for each of the options of the main menu to display:
/* Beginning of class .menuitem */
.menuitem
{
position: absolute;
margin: 0;
padding: 0;
width: 10em;
height: 26px;
overflow: hidden;
}
.menuitem ul
{
padding: 0;
list-style-type: none;
}
.menuitem:hover { overflow: visible; }
/* End of class .menuitem */
/* Beginning horizontal position for each option of the main menu */
#m1 { left: 0; }
#m2 { left: 9.95em; }
#m3 { left: 19.85em; }
#m4 { left: 29.80em; }
#m5 { left: 39.75em; }
/* End of horizontal position for each option of the main menu */
About the above rules, the interesting part is the
overflow
property that will serve to hide the submenus (overflow: hidden) and make them visible when the mouse goes over one of the options
(overflow: visible). Now then, in order for the options of the main menu to be displayed horizontally we used the rule 'position:absolute' in the class menuitem and then manually specified the left offset for each one in the ids (m1, m2, m3, m4).
In the HTML code, each item of the main list needs to be assigned the class .menuitem and its corresponding id. For instance, the HTML code for the first
option of the main menu would be <li class='menuitem' id='m1'>, and for the fourth option would be
<li class='menuitem' id='m4'>. Adding all this to our example, the HTML code would look like
this, and would display
this.
As you can see, we have successfully created a menu with options! This is great progress. But if we want to have various levels of submenus, we still have some way to go, and is where this tutorial really gets interesting :). If you check the HTML of the previous step, you'll notice that the options of the submenus are opening out not at sub-options, but at the same level as the options of the main submenus. What we need is to define the options of the submenus to be displayed to the right of the option that precedes them. So we will add the following CSS rules:
.submenu li { position: relative; }
.subsubmenu
{
position: absolute;
top: 0;
left: 9.95em;
width: 10em;
display: none;
}
.submenu li:hover > .subsubmenu
{
display: block;
}
In the first rule we defined that the position will be relative for <li> elements included within an element of the submenu class
(in this particular case they will be <ul class='submenu'> elements). This way the menus of the subsequent levels of menus will display at the same height as the menu that precedes them. Without this rule, our menu would look like
this.
Next we will define the class .subsubmenu, where we will specify, among other things, the absolute position with relation to the element that contains it (which in this case will be <li> elements which are in turn contained in <ul class='submenu'> elements) and specify a left offset.
In the third rule we define the pseudo-class hover so that when the mouse passes over an option with submenus they will open out. Observe that this
CSS rule will only apply to elements that have the subsubmenu class and that are children of an <li> element that is descendant
of an element that has the submenu class (if you wish to know more about child selectors read over
this section of the CSS specification).
In terms of HTML, each list of the main submenus that will have one or more levels of submenus must be assigned the submenu class:
<ul class='submenu'>, and lists containing the options of the submenus must belong to the subsubmenu class: <ul class='subsubmenu'>. Continuing with our example, the html code would look like
this, and would display
this.
Now we've got a real menu with many levels of submenus!. To make it more presentable we will add some borders. To achieve this, we'll use the following CSS
classes (I got the values by trial and error):
.b_top {border-top:1px solid #369; }
.b_right {border-right:1px solid #369; }
.b_bottom {border-bottom:1px solid #369; }
.b_left {border-left:1px solid #369; }
To each menu option within the <a> element, we will assign the classes we need, separated by spaces. For example, for the second option on the main menu the html code would be <a class='b_top b_right b_bottom' href='#'>, and for an option that, on the design level, comes last on the list, it would be <a class='b_right b_bottom b_left' href='#'>. This a bit tedious, but usually it only needs to be done once when designing the menu. After the
menu is functional, any modifications will be minimal, for instance when adding a new option or a new submenu, as necessary. Continuing with the example,
the HTML with borders would look like
this, and would display
this.
I had some trouble getting all the borders look even because some of them looked thicker instead of overwriting. Therefore, I had to play around with the values of the absolute positions for the options of the main menu (remember that this values were specified in the ids m1, m2, m3, m4).
Well, we have come to the end this tutorial. I hope you find it helpful. By the way, let me point out that with MS Iexplorer this will not work because this
browser does not fully support CSS2. It could work with some tricks and by simulating the hover effect of the list items with Javascript, but better to
use a browser that meets the standards, such as Mozilla, Firefox, Netscape, Opera, etc.
* Note (2005-12-29):
For those who want to have CSS menus with various levels of submenus and that also be functional with IExplorer, check
this page, where it's explained a way to emulate the
hover effect in IExplorer using Javascript. I tried their example menu and it worked in Firefox and IExplorer.
If you have any questions or comments regarding this tutorial send them to <javier@nediam.com.mx>
Greetings to Kukulkan who was the first that showed me the CSS potential.
References:
- Cascading Style Sheets, level 2 - CSS2 Specification - http://www.w3.org/TR/REC-CSS2/
- Petr Stanicek - Hierarchical dynamic menu with CSS - http://pixy.cz/blogg/clanky/csshiermenu/
- Eric A. Meyer - Pure CSS Menus - http://www.meyerweb.com/eric/css/edge/menus/demo.html
- CSS horizontal navigation list - http://www.phoenity.com/newtedge/horizontal_nav/
- Horizontal Drop Down Menus - http://www.alistapart.com/d/horizdropdowns/horizontal2.htm
The latest version of this document is available at: http://nediam.com.mx/en/tutorials/css_menus_advanced/index.php



