Suckerfish Dropdowns
"DHTML" dropdown menus have notoriously involved nasty big chunks of JavaScript with numerous browser-specific hacks that render any otherwise neat, semantic HTML quite inaccessible. Oh, the dream of a lightweight, accessible, standards-compliant, cross-browser-compatible method! Enter Suckerfish Dropdowns.
Meet the markup#section2
To start, we should use the best method for defining a navigation menu — a list. For this example, we will work on a simple HTML unordered list. {Line wraps are marked ». –Ed.}
<ul>
<li>Sunfishes
<ul>
<li><a href="">Blackbanded»
sunfish</a></li>
<li><a href="">Shadow bass</a></li>
<li><a href="">Ozark bass</a></li>
<li><a href="">White crappie</a></li>
</ul>
</li><li>Grunts
<ul>
<li><a href="">Smallmouth grunt
</a></li>
<li><a href="">Burrito</a></li>
<li><a href="">Pigfish</a></li>
</ul>
</li><li>Remoras
<ul>
<li><a href="">Whalesucker</a></li>
<li><a href="">Marlinsucker</a></li>
<li><a href="">Ceylonese remora</a></li>
<li><a href="">Spearfish remora</a></li>
<li><a href="">Slender suckerfish</a></li>
</ul>
</li>
</ul>
Quite straightforward really — nice and neat HTML that, as a result, is highly accessible. But now we want to transform this into a dynamic list — the first level of list items will make up a horizontal menu bar from which the second level lists will drop down.
Styling it#section3
To get started, all of the lists need to be jigged around a bit — namely, the padding and margin set to zero and the list-style set to none:
ul {
padding: 0;
margin: 0;
list-style: none;
}
Now we need to transform the first-level list into a horizontal menu bar. There are a number of methods to do this, discussed in detail elsewhere. We could display the list-items inline (display: inline), but for this example, we are going to float them to the left.
li {
float: left;
position: relative;
width: 10em;
}
The position has been set to relative because we want the position of the second-level, nested lists to be relative to the first-level list items and the width has been set to space it out a bit. The dropdown menu is coming together.
The next step is to tackle the second-level lists that will be the dropdowns themselves:
li ul {
display: none;
position: absolute;
top: 1em;
left: 0;
}
This positions the second-level lists absolutely (pulling them out of the flow of HTML into a world all of their own) and sets their initial state to not be displayed. If you substitute display: none with display: block, you will see the need for the top and left properties in Internet Explorer, because without them, IE will align the second-level lists to the top right of their relative parent rather than the bottom left. Unfortunately, this IE fix will mess things up in browsers like Opera, so add the following CSS to reset the top and left properties on all but IE browsers:
li> ul {
top: auto;
left: auto;
}
And now, making the sucker work. To make a second-level list appear when its parent list item is "rolled over," we simply need to add the following:
li:hover ul { display: block; }
Which says that any list that is nested in a list item that has the cursor hovering over it should be displayed.
Finally, because the lists are floated left, the content underneath it needs to be set free of the floating by applying clear: left to it.
Hold on a minute!#section4
"This dropdown malarkey doesn’t work!" I hear 102.6% (or the latest percentage being thrown about) of you cry. I am, as some might have guessed, talking about Internet Explorer users. The more you use and develop with browsers such as Mozilla the more you realize how pathetic Internet Explorer can be when it comes to web standards. The :hover pseudo class should work with any element, but in Internet Explorer it only works with links. So. What’s the use in a dropdown menu when it only works on -2.6% of browsers? Not much, to be honest. We need to apply a little bit more magic.
DOM-based scripting to the rescue#section5
We’ve established IE’s lack of support for the :hover pseudo class, but by using the Document Object Model, we can attach mouseover and mouseout events to any element. This is good news for us because it means that with a simple snippet of JavaScript we can effectively patch IE’s :hover problems.
Because IE is blind we need to find another way to identify the properties of the :hover pseudo class. With JavaScript, we know that we can manipulate the className property of an element so what we are going to do first is alter the CSS:
li:hover ul{ display: block; }
becomes:
li:hover ul, li.over ul{ display: block; }
Now we can invoke the :hover CSS rules by adding the class over to the desired element. We also need a way to tell IE which of the UL elements on the page we actually want to be our dropdown menus. We can do this by giving an id to our root ul element:
becomes:
startList = function() {
if (document.all&&document;.getElementById) {
navRoot = document.getElementById("nav");
for (i=0; i;
if (node.nodeName=="LI") {
node.onmouseover=function() {
this.className+=" over";
}
node.onmouseout=function() {
this.className=this.className.replace(" over", "");
}
}
}
}
}
window.onload=startList;
On page load, the startList function is invoked. The function determines if the browser is actually IE 5 or greater by checking for the existence of the document.all object and document.getElementById function. This is a bit of a crude way of doing it but it’s short and sweet — and since we are trying to make a compact solution, this will do. It then loops through, enabling mouseover and mouseout events which add and remove the over class from the className property of the element.
There you have it. If you got lost anywhere, have a look at a commented, bare-bones example in action.
Gills, fins, scales...#section6
So far things are a little bare. The idea has been to show the basic workings of the Suckerfish Dropdown, but CSS can make things look a lot prettier. An obvious starting point would be to apply a background color to the second-level lists.
After resetting the top and left properties as described earlier, dropdowns in the pretty example appear directly below menu labels in most modern browsers, but unfortunately not in all. In Safari 1.0, they still drop down from the top left edge of the screen. {Check the discussion forum for workarounds. –Ed.}
Further usability and accessibility#section7
Making links out of the first-level list items will allow tab-stopping for readers who don’t use pointing devices. Pointing those links to higher-level pages than the links in the dropdowns would be even better.
128 Reader Comments
I have created a simple behavior (.htc script) for IE5+ browsers to emulate :hover sub-classes with help of normal classes.
And creted an example for drop-down lists.
You can find it here:
http://www.hszk.bme.hu/~hj130/css/list_menu/hover/index.html
Alrighty. I stumbled across this page last night, and I am happier. I’ve been looking for an ‘easy’ way to do just what this script does.
But, in trying to make NS7.1, IE6, Opera7.23 happy, I am struggling. I’m in the process of re-laying out my site and need to at least get the top2.htm file right so I can include it across the board. That might be wise, but it’s a work in progress.
Can someone tell me why my menu is centered in NS, but not Opera or IE? I think it’s a margin issue…
this be the BSS beast that keeps growing..
http://wanderingproductions.com/images/main.css
and this is the page:
http://wanderingproductions.com/top2.htm
as you can see, the pic is centered. the rest ain’t (outside of NS7). I am… perplexed.
thanks folks
tb
Sean and Dante,
I guess giving out advice is easy from your lofty position. I mean, inventing a browser with no refresh, that doesn’t show you what page you are on and that can’t even spell the month correctly – that’s pretty intense stuff.
http://www.geocities.com/seanmhall2003/homepage.html
Matt
I was working on a DHTML menu when discover this CSS Dropdown and love it. But there’s some things that I wish to know (all about graphical interface):
1. How to implement a css rollover for the first list and a different rollover for the second one. (I have one working great in Mozilla Firebird but doesn’t work in IE6 and Opera 7).
2. Is it possible to use this CSS Dropdown menu to make something like the http://www.mediatemple.net/ navigation?
Thanks in advance.
Noemi, thanks for your tip on Z index! I thought it could be done, just didn’t remember the name for it. 🙂
Now for my next confusion: can these be more than 3 menus/links/divs wide? I noticed the examples using this are just three. Is this because the divs/LIs are float left, none, and right? Could I make more menus by using a table first then put each div/LI in a td and not use the float?
..something like that?
I was wondering if anyone tried pre-caching the background image for the drop down menu, as you would for javascript rollovers.
It would appear that IE is not caching the image, obviously. It is interesting that changing the setting for “check for new versions of cached pages” to anything but “every visit to page” corrects the situation.
It never was necessary for Javascript rollovers, where you would change the source of the image, once the image was downloaded, it remained.
Let me add my congratulations on this clean solution that works so well.
With your UL/LI drop down menu in place, is there a way to structure things so that unordered lists in the text below these menus can be presented in its default (or similar to default) format?
I tried two different approaches to this. Maybe you will have better luck than I did.
First was to give a class (or id) to all the lists used in the menus, with the thought that lists elsewhere in the site wouldn’t inherit the styles for the menus that way. But I didn’t find a way to do that and keep the menus functioning.
Second was make a standard list class with basics defined, so that other lists on the site would use that class and look as defined. This one didn’t work for me, as my lists were coming from a content editable area, and so users wouldn’t know to add the style.
Perhaps those will give you some ideas. Good luck.
A Colleague saw this script on this site ages
ago and sent it to me so that i could try and turn this into a vertical navigation but yet again it’s got bugs! as it doesn’t work properly in IE6 yet it does in all the other browsers that i have tried (can anybody help with this one!