A List Apart

Menu
Issue № 197

Hybrid CSS Dropdowns

by Published in Browsers, CSS, JavaScript, Layout & Grids · 110 Comments

I know what you’re thinking…“Do we really need another article about CSS dropdowns?” Allow me to convince you. What if we could have one clean, well-structured menu which would combine the dynamism and code-ease of dropdown menus and do away with their main problems (not to mention degrade beautifully)? The problems with dropdown menus are:

  1. their secondary options are inaccesible unless you activate the entire menu system; and
  2. they offer insufficient orientation cues for the user. It can be difficult to navigate within a particular section of the site because you have to go back to the dropdown to change pages.
Article Continues Below

This technique is a bulletproof way to ensure browser compatibility and to maintain usability even for people who have old browsers or difficulty accessing dropdown menus, either because of a disability or a low level of comfort with the dropdown paradigm. It also does a much better job than standard dropdown menus of orienting the user within the site.

Warning: This technique will not work as well for lists that require large numbers of items, where dropdowns either shine or collapse under the weight of their own sheer mass, depending on your perspective.

We’re going to create a hybrid menu that runs horizontally across the window. It has two levels of navigation (in our example, main topics and their associated pages). Our menu will allow for dropdown access to all pages in both navigation levels, display the current choices in the selected topic area constantly, keep the user aware of where he is in the site, and be clean and light to boot.

Sound good? Let’s get going.

First, we need a list

Let’s start with a list of architectural periods and some of their major representatives. We will attach an ID to the

    element and classes of “off” and “on” to the main list items (which is probably not the best solution, but will work for this article’s purposes):

    Get some style on

    This is a great place to start. Simple, semantic markup that can be maintained easily and in one place. It looks like you would expect it to look.

    The first thing we are going to do with our CSS is to display the primary level horizontally (using float) and hide all of the subnavigation lists. We will also set the display for the links in the list to be bold, colored, and have a border.

    #nav li {
      /*float the main list items*/
      margin: 0;
      float: left;
      display: block;
      padding-right: 15px;
    }#nav li.off ul, #nav li.on ul {
      /*hide the subnavs*/
      display: none;
    }#nav li a {
      /*for all links in the list*/
      color: #f90;
      font-weight: bold;
      display: block;
      height: 15px;
      width: 100px;
      border: 1px solid #29497b;
      padding: 5px;
    }
    

    Next, let’s position our second nav level below the main list, so that when it does show up again, it’s in the right place.

    #nav li.off ul, #nav li.on ul {
      /*put the subnavs below and hide them all*/
      display: none;
      position: absolute;
      top: 33px;
      height: 15px;
      left: 0;
      padding-top: 10px;
    }
      
    

    Finally, we’ll show the subnavigation bar for the active topic area, “Modern.” The best way to do this if you want only one central, complete menu file, is with a body class of, say, “Modern,” and corresponding selectors. For this article, which will be published in someone else’s body element and should remain self-sufficient, we have set a class of “on” to the active topic and “off” to the inactive topics.

    #nav li.on a {
      /*change border color for active topic area*/
      border: 1px solid #f90;
    }#nav li.on ul a, #nav li.off ul a {
      /*  cancel inherit of border
          on subnav of active topic */
      border: 0;
    }#nav li.on ul {
      /*display active subnav list*/
      display: block;
    }
    

    After adding a couple borders, you can see what we have so far here.

    So they all rolled over and one fell out…

    Now, we activate the rollover. This is not much different than any other CSS dropdown menu—the hover is on the li element, so IE will choke due to its poor implementation of the :hover psuedo-class. We’ll get to that in a minute. The following CSS removes the border on the second nav level, sets the active subnav to always display, and sets the inactive subnavs to display when their parents are hovered. We’ll set a z-index to be sure that the hovers always take precedence over the current topic area’s subnav.

    #nav li.on ul a, #nav li.off ul a {
      float: left;
      /*ie doesn't inherit the float*/
      border: 0;
      color: #f90;
      width: auto;
      margin-right: 15px;
    }#nav li.on ul {
      /*display the current topic*/
      display: block;
    }#nav li.off:hover ul {
      /*  display the other topics when
          their parent is hovered */
      display: block;
      z-index: 6000;
    }
    

    We’ll make it a little more user-friendly, with a background on the hovered tabs.

    #nav li.off a:hover, #nav li.off:hover a {
      background: #29497b;
      color: #f90;
    }  
    

    Check in on our progress here.

    Accommodating our “special” browser friends

    You have two options for users of browsers such as, say, Internet Explorer, which lack support for :hover on anything but the <a> element. You can either leave the menu as is, knowing that it will work beautifully in a few years when all browsers support :hover (knock on wood), and resting in the knowledge that all the navigation options will be visible and accessible to all users, or you can work up a bit of JavaScript to rewrite the CSS selectors in language IE understands to make the dropdown dynamism accessible to all.

    (Working in IE as-is being relative.) We do have to adjust the positioning a little using the asterisk hack:

    #nav li.off ul, #nav li.on ul {
      /*put the subnav below*/
      top: 33px;
      *top: 44px; /*reposition for IE*/
    }

    So it’s working beautifully in modern, standards-compliant browsers, and working in a functional, degraded way, with correct positioning, in versions 5 and 6 of Internet Explorer. With a little help, we can make the hovers work in IE. This simple JavaScript rewrites the hovers as mouseover events, and works for all versions of IE/Win 5x and 6x. Much thanks to Patrick Griffiths and Dan Webb, whose “Suckerfish Dropdowns” got me started with CSS-based menu systems. Their snippet of JavaScript looks like this:

    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;
    

    With a simple change to the CSS:

    #nav li.off:hover ul, #nav li.over ul { 
      display: block;
      z-index: 6000;
    }#nav li.off a:hover,
    #nav li:hover a,
    #nav li.over a {
      background: #29497b;
      color: #f90;
    }

    to add the class “.over” to the list items that require hovering, the list functions just as well for you IE/Win users. Not that you shouldn’t think about a new browser, but for now we’ll continue to support the masses in the manner to which they’ve become accustomed.

    So that’s it. An incremental change, perhaps, over the previous work with CSS dropdowns, but another angle for you to explore for those instances where it really is useful to have the navigation options displayed rather than hidden inside a dropdown menu.

    But can it be beautiful?

    Ok, that’s not it. I couldn’t leave you without a slightly more graphically-rich version to take this technique out of the pedantic and into the real world. With a few changes, a CSS sprite navigation image (thanks, Dave Shea), a photo I took in NYC, and a bit more CSS, we get a menu system which really shows the power of CSS combined with graphic design. Check out the final Hybrid CSS Dropdown, fully functional in all modern browsers.

About the Author

110 Reader Comments

Load Comments