History

Early implementations of dropdown menus on the web were clunky at best, full of obtuse HTML and excessive JavaScript. This all changed when Patrick Griffiths and Dan Webb introduced Suckerfish Dropdowns in 2003, which has since become an indispensable tool for web designers.

Just as the web landscape has evolved, so has the Suckerfish Dropdown. So here’s my take on a modern implementation, along with a few tweaks I’ve picked up over the years.

Update: The web has evolved. Suckerfish dropdowns are no longer considered best practice.

The HTML

The Suckerfish Dropdown uses semantic HTML. To see what I mean, take a look at the HTML for the nav menu example we’ll be using:

<ul id="nav">
  <li><a href="#">Home</a></li>
  <li>
    <a href="#">Shows</a>
    <ul>
      <li><a href="#">30 Rock</a></li>
      <li><a href="#">Doctor Who</a></li>
      <li><a href="#">The IT Crowd</a></li>
      <li><a href="#">The Office</a></li>
      <li><a href="#">Star Trek</a></li>
    </ul>
  </li>
  <li><a href="#">About</a></li>
  <li><a href="#">Contact</a></li>
</ul>

Pretty easy to follow, eh? Instead of using a nonsensical pile of divs and spans, we’re using appropriate list elements to convey meaning. This semantic HTML makes it much easier to understand what’s going on.

We haven’t gotten to the CSS yet, but check out what we have so far.

DemoSuckerfish Round 1

Boring, but still completely readable. This is another great perk of Suckerfish Dropdowns: graceful degradation.

The CSS

Now we use CSS to add formatting and the dropdown sub menu effect. That’s right, we’re not using a JavaScript mouseover. Instead, we’re using a pure CSS solution via the :hover pseudo-class.

Let’s step through the CSS.

First, we put an overflow on our outermost <ul>. This ensures that any content after our nav menu will be pushed below it.

#nav {
  overflow: auto;
}

Now we change the default vertical bulleted list layout to a clean horizontal layout. And the line-height declaration helps nested sub menus line up correctly. That’s right, when we’re done, you’ll be able to nest as many sub menus as you want.

#nav, #nav ul {
  list-style: none;
  padding: 0px;
  margin: 0px;
  line-height: 1em;
}
#nav li {
  float: left;
  width: 100px;
}

Sub menus need to be hidden at first. This is accomplished by positioning them way off the left side of the page, out of view. We do this instead of display: none for the sake of screen readers.

#nav ul {
  position: absolute;
  left: -999em;
  width: 100px;
}

Next is the hover effect that makes a sub menu visible by resetting the left offset. Notice we use the > child selector to display only the immediate child sub menu, rather than all descendant sub menus.

#nav li:hover>ul {
  left: auto;
}

Time for the obligatory IE fixes. The first style keeps sub menus from sticking open. The second style ensures first level sub menus appear below the menu items that opened them.

#nav li:hover {
  position: static;
}
#nav a {
  display: block;
}

Finally, we tweak the margins on nested sub menus so they appear directly to the right of the menu items that opened them.

#nav ul ul {
  margin: -1em 0px 0px 100px;
}

The JavaScript

Here’s the best part. There is no JavaScript! Hooray!

The only caveat is with (surprise!) IE. By default, IE won’t apply the :hover pseudo-class to non-hyperlink elements, like <li> in our case. In the past we’d use JavaScript to fake it, but this is no longer necessary in IE7+. By adding a doctype declaration above the opening <html> tag, we can put IE into standards mode, which triggers the correct behavior. This is easy to do, and something you should always be doing anyway:

<!DOCTYPE html>
<html>
  <!-- the rest of your HTML -->

Wrap Up

At this point we have a fully functional nav menu with a dropdown sub menu that works in all major recent browsers. Check it out.

DemoSuckerfish Round 2

All that’s left to do is style as wanted. And as mentioned before, you can add as many nested sub menus as you want. Check out the culmination of this tutorial below.

DemoSuckerfish Round 3