ADxMenu2 - pure CSS multilevel menus
A new way to accomplish multilevel menus without the Javascript. Peter Nederlof has created a behavior that adds arbitrary :hover support for IE/Win. That means that IE will now recognize li:hover
, thus giving us more degrees of freedom for menu creation.
That means that basic functionality does not depend on user’s preference regarding Javascript. You can include the script only if you want to add additional usability features: off-screen positioning check, as well as placing menus over drop-downs (WCH).
I have slightly changed the way CSS is built. All of them now depend on li:hover
. Peter’s behavior deals with IE’s lack of support, but only on windows. That means that ADxMenu 2 menus will not work in Mac IE, Opera 6 and any other browser that doesn’t fully support :hover pseudo-element.
Major improvement: this CSS finally solves the “phone-book” problem in IE/Win, and sub menus can be normally repositioned, not one over another.
Pure CSS menus
First rule is to include the behavior (with proper path, relative to the .css file).
body { behavior:url("csshover.htc"); }
Then we remove the default list rendering and reset all box properties to 0. List items are floated to the left and positioned relatively, to define new coo-system for their child elements (sub menus).
#menu ul {
margin: 0;
padding: 0;
border: 0;
list-style-type: none;
}
#menu li {
margin: 0;
padding: 0;
border: 0;
display: block;
float: left;
position: relative;
}
A elements are transformed to blocks. For the sake of, and only for, IE/Mac, they are also floated to the left, using famous backslash-commented hack. All sub menus are absolutely positioned.
#menu a {
float: left;/* */float: none;/* */
display: block;
}
#menu li ul {
visibility: hidden;
position: absolute;
z-index: 10;
}
The next part is crucial to avoid “phone-book” problem. Sub menu list items are pos:relative for anything but IE. This way, there are no new z-index “spaces” and sub menu will not go under the remaining items in the parent menu. Only when the sub menu is about to be displayed, its parent item is going pos:relative, allowing …
#menu li ul li {
width: 100%;
display: block;
float: left;
position: static;
}
html>body #menu li ul li {
float: none;
position: relative;
}
#menu li ul li:hover {
position: relative;
}
…correct placement. Without the li:hover support, this is not possible. Peter deserves eternal gratitude for this.
#menu li ul li ul {
top: 0;
left: 100%;
}
Unfortunately, there is no way to simulate the child selector, also needed for nested menus. Therefore, we need to cheat a little.
div#menu li:hover ul, #menu li:hover li:hover ul,
#menu li:hover li:hover li:hover ul {
visibility: visible;
}
#menu li:hover li ul, #menu li:hover li:hover li ul,
#menu li:hover li:hover li:hover li ul {
visibility: hidden;
}
That first div is needed for IE5.x support. Last version of behavior should actually work without the actual node name specified. And it does in IE6. Also in some simple cases in IE5, but not in this. Strangely, adding just one node name solves the problem. Peter said to me that it must be some error in .htc, but I believe that it’s some IE5.x quirk.
For advanced browsers, that understand child selector, use it instead of the code above. This part can actually be removed, but I deliberately included it to show the proper way how this should be done.
#menu ul#menuList li:hover > ul {
visibility: visible;
}
That is all. This is enough to make the menus work. Rest of the styles is up to your CSS and designer skills. Take a look at the examples I provided.
Read the next ADxMenu entry to learn about the script that will reposition the menus if they go outside the visible window area. It also incorporates WCH2 script which helps with yet another IE/Win problem.
Update (May 1st): After few questions in the comments, I realized that I haven’t explained the logic behind the IE simulation of child selector. So, here it goes.
How to add more submenu levels for IE
If you look at the unstyled version of the list, you can see that, when particular item is hovered, we want the first nested list to appear, and all deeper nested lists to be hidden. This is possible with child selector - it will take all elements inside of the li
and fetch just first ul
it encounters:
#menu li:hover > ul {
visibility: visible;
}
In example list, when mouse is over Products, browser’s CSS engine will find that li
and then start to process all elements inside of it. It first finds a
, skips it, and then finds ul
, applies the style to it and stops.
Since IE doesn’t support this, we need to simulate it. Look at this:
#menu li:hover ul {
visibility: visible;
}
#menu li:hover li ul {
visibility: hidden;
}
Look at the HTML code for the list and work it out down the nesting. #menu
starts it up and then goes to process all its children. It skips the ul
for the main menu, and then finds the Products list item. li:hover ul
means “find all UL elements inside of this LI and apply the style to all of them”. And thus not only Products submenu is visible, but also Categories and Hardware. So we need another rule to leave Products visible, and hide those that are nested deeper.
The other rule has one more li
before the ul
. That means that browser will find all ULs nested inside each list item in the Products submenu and hide them.
To be simple and clear: when you load the page, only main menu is shown. When you mouse over Products item, first rule will show all possible submenus - for Products, Categories and Hardware. What we actually need is just Products submenu to appear. Second rule makes sure that Categories and Hardware submenus are hidden.
For deeper levels, you need to repeat this rule pair and each time add one more li:hover
just after #menu
. Here is how it looks for one level down (Categories).
#menu <strong>li:hover</strong> li:hover ul {
visibility: visible;
}
#menu <strong>li:hover</strong> li:hover li ul {
visibility: hidden;
}
Since I have 3 submenus (main menu is not counted), in all .css files I have 3 pairs of rules.
Or, to cut the long story short - number of li:hover
is identical to submenu level.