Four variants of drop-down menu
It seems like years…
Having established (or so I thought) the basis for good, versatile, menu system, I figured it’s about time to make it truly powerful. I wanted it to:
-
be placed anywhere and to open-up in any direction
-
work in CSS2-capable browsers and IE5+/Win
-
provide b-version script for use with other browsers (mostly IE5/Mac)
-
circumvent the drop-down problem on IE/Win
-
avoid scroll bars upon page load (while menus are hidden)
-
prevent IE from drawing tall submenus over prev/next parent menu options
Well, I did all that, and I did none of that.
Basis
For those of you late-arrivals to the party, this menu uses nested link list as its HTML, and then uses a bit of CSS to provide the drop-down menu.
Then, on top of that, it uses a bit of Javascript for the following tasks:
-
prevent HTML select boxes to go over submenu, in IE/Win (using my WCH script)
-
keep the menu inside the visible portion of the window area (reposition)
First task was thought to be easy. WCH script, in its 3rd version, is stable, heavily tested and used on a variety of web sites I built. Well, while implementing some of the requirements for this version, IE caught me with my pants down. Read on to see how.
Second task dragged me into the hell of incomplete CSS-P/DOM implementations and an amazing array of browser bugs. The most amazing part was that browser with least problems (if any) was IE/Win. All those nice, modern, standard-compliant browsers were tripping me every inch of the way. Read on to see how.
In some cases, even the mere task of drawing the main menu was not possible (now, this was IE).
Version 2.5
I thought that this would be version 3, but due to unfulfilled goals, it had to be downgraded.
The HTML structure remains the same: nested ULs, with As in each LI. For IE/Win, csshover.htc
is used to simulate li:hover
.
CSS has changed a bit, but not that much. I’ll repeat the basic instructions, and leave you to dissect the examples and build your own designs.
Main example for this version is using WinXP look, and I provided the following variants:
-
HBT: horizontal main menu, submenus drop-down (top to bottom)
-
HTB: horizontal main menu, submenus drop-up (bottom to top)
-
VLR: vertical main menu, submenus drop-right (left to right)
-
VRL: vertical main menu, submenus drop-left (right to left)
This is the only menu style featuring all variants, both pure-CSS and scripted. I picked this one because it’s the most complicated style I could think of. Its items (LI) have margins, paddings, borders and icons - all possible causes for various (re)positioning problems. If this is ever made to work properly, then any other style would work by itself.
All other examples have only HTB variant; the task of keeping all of them up-to-date is taking way more time then I’m able to put aside for this.
HTML code
HTML is very simple so I won’t go into details. I only want to emphasize one important bit I kept forgetting in previous write ups. Wrapping div
and each ul
must have an id
attribute. It is essential for WCH script to function properly. Also, you must place class="submenu"
on each a
element followed by nested list.
CSS core
This is brief walk-through of the CSS build-up for all menu variants. In actual menu’ .css files, this part is denoted as Basic style.
Horizontal main menus are made of floated list items, so if you want to have a background/border on it, you need to provide the appropriate height
value. Since this is highly dependent on design (done in Make-up part of the .css), you should do it there.
For vertical menus, same counts for width
, or your menu would take up all available width.
#menu {
/*
height: ??; Hxx
width: ??; Vxx
*/
display: block;
}
Then remove all list-specific attributes. List items are blocked, floated and relatively positioned, so that submenus can happily open next to their parent item. float:left
part is not needed for vertical menus, but that can depend on your design and HTML code.
#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;
}
Then turn links into blocks, to have more fun when styling. The second rule is for IE, mainly IE6 and it is there to fix its inability to expand clickable area. Although it visually do that by itself, link-click functionality exists only on actual text of the link. It seems that changing position
fixes this - great find by Douglas Clayton.
#menu a {
display: block;
}
* html #menu li a {
position: relative;
}
All submenus are hidden initially. Best way to do this is to completely remove them, using display:none
, so there are no unnecessary scroll bars. It works as it should, for all but IE due to a whole array of problems, explained further below.
#menu li ul {
visibility: hidden;
position: absolute;
z-index: 10;
}
html>body #menu li ul {
display: none;
}
Each submenu list item should be 100% wide, so that menus look like menus. Again, thanks to wonderfully non-practical W3C box model, this is possible only if you don’t have paddings on LIs. If you do (like I have in WinXP style), then you need to tweak this in the make-up part.
#menu li li {
width: 100%;
}
Now comes the part responsible for menu acting as menu. First, set the position of the submenu, in respect to its parent item and in respect to direction in which you want it to open up.
For horizontal menus, 1st submenu will open as needed, by default. The rest of them needs a tweak. For HTB variant, it should open on the right of the parent item, so it goes like this:
#menu li li ul {
top: 0;
left: 100%;
}
For HBT (drop-up), bottom edge of the 1st submenu needs to be moved up a bit. The value of bottom highly depends on the make-up style, so be ready to tweak it as needed.
#menu li ul {
bottom: 1.5em;
left: 0;
}
#menu li li ul {
bottom: 0;
left: 100%;
}
VLR variant is the same as HTB, expect the selector is one way down - it applies to the 1st submenu as well.
This also illustrates the solution for the problem that sometimes appears, when on initial page load huge (more or less) horizontal scroll bar appears. This way, at initial page load, all menus are in top-left corner (of the page or of the parent list item). This can be applied to all variants, not just VLR.
#menu li ul {
top: 0;
left: 0;
}
#menu li:hover ul {
top: 0;
left: 100%;
}
Finally, for VRL variant:
#menu li ul {
top: 0;
left: 0;
}
#menu li:hover ul {
top: 0;
left: -100%;
}
Last bit is show/hide. Since IE does not support child selector, we need to simulate it. Add as many levels of this, as needed. Number of lines (or number of li:hover
s) should equal the nesting level.
div#menu li:hover ul,
#menu li:hover li:hover ul,
#menu li:hover li:hover li:hover ul {
visibility: visible;
}
div#menu li:hover li ul,
#menu li:hover li:hover li ul,
#menu li:hover li:hover li:hover li ul {
visibility: hidden;
}
The following rule is for browsers supporting CSS2 selectors, with increased specificity (to override the previous simulation for IE).
#menu ul#menuList li:hover>ul {
display: block;
visibility: visible;
}
Examples
As usual, examples can be found here, also linked from the side column. As said previously, WinXP style offers both pure CSS and scripted version. First ones should be used on simple pages and with simple menus. If your page has select boxes or you have long menus, then you are better off with scripted version. Scripteds are an upgrade of the pure CSS variant - you just add links to two scripts and the following line of Javascript:
ADXM.Add( "menuList", "H" );
First parameter is the id
of the main UL (not the wrapping DIV). Second parameter is either “H” or “V”, specifying the type of menu. It is optional - if this is anything other then “V”, it will be treated as “H”.
Also, for CSS validity reasons, note that csshover.htc is removed from the .css and placed inside of IE’s own conditional comments:
<!--[if lte IE 6]>
<style type="text/css">
body { behavior:url("csshover.htc"); }
</style>
<![endif]-->
This is an example - you make sure that path from HTML document to .htc is correct.
There is also an example of b-version of the script. Here, the script takes the role that csshover.htc had - this can be used on long pages (where .htc freezes the page) and if you need to support IE5/Mac (or any other browser without li:hover
support).
Weirdness, anger, despair…
For the longest time, I thought is it really worth it to publish this. What won me over is the fact that best menus are simple menus, where most of problems would not show and when menus actually work quite nice.
For the sake of information and for the hope that some of you might solve one or few of the problems, here it comes. First the problems appearing in the pure CSS, and then those in the scripted version.
Opera 7.5x weirdness (pure)
When 3rd level submenu is opened in Opera, you get the display as shown. I tried this with latest Opera (7.52). It happens with all variants, in pure and scripted version. You don’t even need to move the mouse over that submenu - hover its parent item (Hardware) then move the mouse down to Electronics - the submenu will stay visible in its messed form.
If some of you don’t find a solution, I will post this as a bug to Opera Software.
Box model problems in IE5.0
Due to different (better if you ask me) box model, IE5.0 has slightly different display. Shorter item is due to padding-left:29px
. I did not want to fix this, as it is not creating functionality problems.
Also note the taller main items, because of white space bug. To fix this, you need to either float the items or connect list items on one line (in .html).
This is probably happening in 5.5 two, but I had no chance to even take a look at it, because…
Vertical weirdness in IE 5.5
…of this fine behavior in it.
No comment.
HBT impossible in IE5.0
This is the problem I covered before. If you replace visibility
with display
, then it sort of gets itself in order. But IE has other problems with display…
Safari bug 1
Horizontal menus in Safari have no background on hover, because the main menu items are floated. If you remove the float (or take a look at vertical variants) then all is well. My Safari is 1.2.2 (v125.8).
Background color does appear when Safari is forced to withdraw the screen; if you open Hardware submenu, scroll bar usually appears and then highlighted state shows up.
Safari bug 2
In the first tests, when I was playing with the following combination:
#menu li { position: static; }
#menu li:hover { position: relative; }
…I got another problem with Safari. It will show the submenu properly, but fail to remove them from the screen on mouse-out.
Overlapping in IE
The best part from the previous version is now lost. The CSS bit from above (in Safari bug 2), had one fabulous benefit in IE: it prevented idiotic overlapping of the menus. But the fact that items were static caused their submenus to be initially placed outside of the screen, thus introducing hor. scrolls.
Horror is totally unleashed in scripted version.
Why did I change this back to lousy variant? Because of Safari bug? No. Read on.
overflow:auto (scripted)
If you have a rather long menu, and an unusually small screen, then you might end up in the situation where your submenu is taller then available viewport (same applies for very wide menus). Natural thing to try here is to set the submenu’s overflow
property to auto
, enlarge the width (height) of it for some safe value to cover the width of the scroll bars and then let the user scroll.
Not possible my brother - as soon as overflow is applied, all browsers fire mouseout event and your menus disappear.
Firefox and Opera, HBT (scripted)
Firefox and Opera 7.5 were the only browsers (outside of IE) where repositioning worked as it should. Except this problem with drop-up menu (screen shows the state after repositioning). The visible part finishes at the exact point where bottom edge of the submenu was, before it was moved down.
Since both browsers behave identically, this might be my error, although I can’t seem to find it.
NN7.1 and Safari (scripted)
Both of these had to be excluded from the script, because of incomplete DOM implementation. Safari has even worse problems, ones I can only describe as erratic. It still has a long way to go. Don’t know about you Dave, but to me this seems more important than proprietary HTML extensions.
IE mayhem
Welcome.
Remember this entry? IE = P(iece) o(f) C(rap)? Here is the drop(s) that pushed me over the top.
Before all, comes the simulation of li:hover
with csshover.htc and simulation of li>ul
, as explained in the CSS walk-through above. That .htc is basically Javascript, run on page load. As such, it takes some time to be executed, so it can’t be used on very long pages.
What it does is that it adds onmouseover
and onmouseout
event handlers for LI in the menu and also dynamically creates special classes that are identical to li:hover
rules in the CSS. First handler changes the class of the LI to corresponding class. That also takes time. It might be milliseconds, but it still takes time. Remember that.
Now one part of the answer why I gave up the solution for overlapping.
Changing the position
property for the element is also done in runtime, in about the same moment as previous class switch is done, right after that actually, if I’m not mistaken. Remember this as well.
Now, the second part of the answer.
My ADxMenu.js script, adds another onmouseover/onmouseout pair of handlers. For IE, those handlers call WCH.js (to hide select boxes), as well as repositioning function. Both WCH and repos needs to know what coordinates they will use when calculating the menu position. Third remember.
Well, if you combine those three bits I told you to remember, you will get highly unstable situation, where God knows in what order they will be executed. They need to be done in the exact order I lay them out here, or the repos will not work and WCH’s iframe will be created in the wrong place.
Completely uncontrollable situation. But the show does not stop.
It goes on when you try to use display
instead of visibility
. In order for any element to have a programmatically fetchable size and position, it must have display
value other than none
. My .css changes this value in runtime, on mouse over.
Remember those three bits again and add this one. Apparently IE can’t do class switch in time for the repos and WCH scripts, so they happily return zeros or nothing for the submenu’ size and position. Lovely. You try to fix one problem and bump into another. And another. It never ends.
I tried several tricks.
Changing the position using JS, not CSS, did not helped at all. It behaved the same. Changing WCH to check the runtime value of display
property (and if it is not, to force it) yielded very interesting results: returned value was block
, but it behaved as it is not. This was too much.
All in all, this is one hell of a mess. Use it as you dare, on your own risk. Be prepared to anything. And please report back any ideas and solutions you might have.