Suckerfish Dropdowns

A note from the editors: While innovative for its time, this article no longer represents modern best practices.

“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.

Article Continues Below

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:

    Now that we have a means of identifying the root ul element of our dropdown list, we can grab this element and loop through all of its child elements, attaching mouseover and mouseout events to all the li elements nested within it. And this is how it’s done:

    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.

    About the Author

    Dan Webb

    Dan Webb is a web developer and wannabe DJ. His recent work includes implementing standards-based, accessible sites and web applications for UK government bodies and making people dance around in murky London bars.

    Patrick Griffiths

    Patrick Griffiths is a freelance webmaker based in London who has a penchant for soul music, evolution and walking his pet website, HTML Dog. He sometimes prefers the moniker PTG, depending on what mood he’s in.

    128 Reader Comments

    1. 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

    2. 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.

    3. 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?

    4. 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.

    5. 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?

    6. 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.

    7. 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!



      Test CSS Vertical Navigation



    8. Hi,

      I’ve been playing around, trying to find some way of making the second level be displayed horizontally, and I’ve come to the conclusion that I can’t, at least, not with the Suckerfish and my knowledge of CSS… Any ideas? The problem is that the width of the parent li has to be wide enough to hold all the child lis, but narrow enough to be a tab. Maybe I could slide each tab across, so that they overlap, but then it needs to know which tab it is.

      Thanks,

      Will Thompson

    9. Thanks so much for this great tutorial! It’s a holy grail, that’s for sure.

      I’m using the menu system for a design in process, and I encountered something that should be noted for others who also wish to use this system.

      If your menu drops down over any element that is positioned relatively, the dropped down menu disappears behind that relatively positioned element in IE/6 Win, regardless of what z-index you give the menu.

      Thanks again! It’s awesome.

    10. You sholud give a higher z-index and relativ position to the parent element of your menu which is on the same level or higher then other positioned elements.

      [div id=”positionedelement” style=”position:relative;z-index:1″]
      [/div]

      [div id=”menu” style=”position:relative;z-index:2″]
      [ul][li]list1
      [ul]submenu[/ul]
      [/li][/ul]
      [/div]

      [div id=”positionedelement” style=”position:relative;z-index:1″]
      [/div]

    11. The menu doesn’t seem to work in Moz/Firebird/Net when you use the “overflow: auto;” css-style on the content below. If you hover over the links with these browsers, the menu hides when you move the coursor over the content below. In Explorer and Opera it works fine…

      Check it out:
      http://www.acc.umu.se/~caran/barebone.html

      I dont know if its a flaw in the code or the browser… Do you know?

    12. Ok so in the example, the first element in the list has a clickable region that is the entire box, not just the text. The ones bellow the first element in any of the drop downs, has a clickable region of only the text, is there a way to fix it so that all of the links in the drop down have a clickable region of the entire box and not just the text?

    13. Really like the menus, already got plans to implement them in one site, and will be keeping an eye on this site for the more drop down levels. That would make the script excellent.

      Will try and see if I can help out with that.

    14. My menu flickers in Netscape 6.2.3. It seems like the only solution here is to wait for browser updates. Has anyone found an answer to this problem yet?

      Dagmar

    15. The Suckerfish example doesn’t seem to work on Netscape 6.2.2 on MacOs9. Has anyone else experienced this? Is it just my machine or is it a problem with that version of the browser? Has anyone found a workaround?

    16. Previous comments have pointed out that there’s a big problem in Safari 1.0, although the dropdowns work fine in Safari 1.1 (apparently, Apple fixed the bug). From reading all the comments, the Safari 1.0 problem seems to be the worst issue with the menu.

      Here’s a workaround. This uses JavaScript to detect Safari 1.0, and keeps the second-level menu hidden from that browser only.

      Insert the following immediately above the tag:

      You could put the JavaScript in a separate file, of course, but the reference to it needs to be located *after* the CSS, in the .

      I copied most of these lines of code from the Browser Detect script by Chris Nott, which is available under a Creative Commons license at http://www.dithered.com/javascript/browser_detect/

      Note that (1) the version number in Safari’s user agent string is the build number, not 1.0 or 1.1; and (2) I don’t know the correct number to use in the script. 87 may not be the best. All I know is that I’m using Safari 1.0.1 (with OS 1.2.8) and it is version 85.6. I don’t know what version was the first for Safari 1.1. (I didn’t see it in a quick look at Dave Hyatt’s blog.) If someone knows the answer, please post it.

      Using the above code, Safari 1.0.1 displays only the top-level menu with working links, while the dropdown menus work in other browsers.

    17. Corrections: The title of my post should have been “Safari 1.0 workaround” (not 1.1). And I’m using Mac OS X 10.2.8 (last Jaguar version).

      Also note that I’m not a JavaScript wizard, so someone may be able to improve the code.

    18. If someone has a vertical popup/”dropdown” menu that is ready for prime time, I’d love to see it; and use the code, if you’re willing to share.

      I’ve been trying to create a vertical version, where the second-level menus open to the left. After spending a couple days, including looking at a few prominent CSS sites for ideas (including Listamatic), I nearly succeeded, but have given up. From what I can tell, browser support is not good enough yet to implement this on a business web site for a paying client.

      Eric Meyer came close
      http://www.meyerweb.com/eric/css/edge/menus/demo.html
      But it only works in some browsers, and some important details are not so great (on mouseover, the menu “title” disappears from top-level menu). Not good enough for my needs.

      Given that a CSS-only solution is NOT ready for prime time, I would like to find a JavaScript substitute that meets ALA’s usual criteria: high-quality, high standards-compliance, lightweight, portable, customizable. Of course, it needs to work in nearly all browsers, especially IE/Win.

    19. Is there any way to link to an external stylesheet that will do the same thing? I’d like to use this IE hack, but I’m trying to keep my web pages clean.

    20. Larry’s aforementioned code seems to grab the “spoofed” Mozilla version number (which is “5”) rather than the Safari build number. As a result, using his code appears to disable the drop down functionality for all versions of Safari, not just the pre-Panther versions.

      I managed to cook up the following code which appears to grab the correct build number. Note that Javascript is not at all my specialty, so some of you will undoubtably find “opportunities for improvement.” 🙂

      // Sniff pre v.86 versions of Safari

      var agt = navigator.userAgent.toLowerCase();
      var appVer = navigator.appVersion.toLowerCase();

      var is_safari = (agt.indexOf(“safari”) != -1);
      var build = appVer.substring(appVer.lastIndexOf(“/”)+1);

      if (build < 86) { document.write('

      ‘);
      }

    21. Fixed a couple of problems for you and got it to validate. I got it to work as (I think) you intended in MSIE 6.0, Netscape 7.1, Opera 7.23, Mozilla/Firebird 0.7, K-Meleon 0.8.2. The main to the css changes were:

      li {

      Clear: left;
      float: left;
      }
      ul>li{
      clear: none;
      float: none;
      }

      li > ul {
      top: auto;}

      See: http://whistlingduck.wrellis.com/menu4.html

      I am still having issues with my site on Mac browsers. Unfortunately I do not have easy access to a Mac so turnaround is slow. If anyone has any advice I would sure appreciate it.
      -William Ellis

    22. dont forget the doctype:

      i was using Transitional and it wouldn’t work in Mac/IE. once i changed to strict, everything worked great.

    23. I’m working on a website for school and my teacher refuses to give up frames. He wants a drop-down menu in a typical side-bar fashion. After trying this example, my drop-downs go outside of the frame so they aren’t much good.
      I was looking at two solutions, and was wondering if anybody here could help me with either of them.

      Solution 1: Code the drop-downs in such a way so that the drop-downs push the lower options out of the way, in the same column. Example(hope this works):

      Heading 1
      Heading 2
      Heading 3

      And when mouse hovers on Heading 2, you get:

      Heading 1
      Heading 2
      Option 1
      Option 2
      Heading 3

      You get the idea, right?

      Solution 2 (may be easier?): His main objections to tables instead of frames is that it’s easier to maintain one menu file. So, I was wondering if it would be possible to add in a special HREF that would embed the code for the menu into the table, without having to copy it again. I hope you can understand what I mean…

      Thanks for the help, and I’m finding this to be a great site for information.

    Got something to say?

    We have turned off comments, but you can see what folks had to say before we did so.

    More from ALA

    I am a creative.

    A List Apart founder and web design OG Zeldman ponders the moments of inspiration, the hours of plodding, and the ultimate mystery at the heart of a creative career.
    Career