Developer

Creating a three-level rollover vertical menu with CSS

In this tutorial, we'll build a three-level rollover menu that expands vertically when the user moves their mouse over the items.

Using CSS and HTML to create menus is simple and efficient, as it allows you to build and expand the menu by adding new levels and items easily. In this tutorial, we'll build a three-level rollover menu that expands vertically when the user moves their mouse over the items.

To grasp the ideas presented here you will need some knowledge of HTML unordered lists and CSS

The final outcome we are aiming for is:


Firstly, we need to create some space on the main HTML page to hold the CSS menu.


body {
  margin:  0;
  padding: 5px;
}


#menuContainer {
  background-color: white;
  width: 10em;
  padding: 5px;
}

Notice that we are using "em" to set the width of the container, not px. The reason for this is that if the user chooses to resize the text in their browser, then the size of the elements will change as well to accommodate the text.

This container style will be applied to an HTML <div> element:

<div id ="menuContainer">

Inside this div element we need to put some nested ordered lists to create the structure for our menu. The code below shows the three levels we are going to implement.



<div id ="menuContainer"> 
<ul>
  <li><a href="#">Books</a>
    <ul>
      <li><a href="#">Fiction</a>
        <ul>
          <li><a href="#">Classics</a></li>
          <li><a href="#">Romance</a></li>
          <li><a href="#">Horror</a></li>
        </ul>
      </li>
      <li><a href="#">Non-Fiction</a>
        <ul>
          <li><a href="#">Military</a></li>
          <li><a href="#">History</a></li>
          <li><a href="#">Gardening</a></li>
        </ul>
      </li>
    </ul>
  </li>
  <li><a href="#">DVD</a>
    <ul>
      <li><a href="#">Action</a></li>
      <li><a href="#">Comedy</a></li>
      <li><a href="#">Thriller</a></li>
    </ul>
  </li>
  <li><a href="#">Games</a>
    <ul>
      <li><a href="#">Board games</a></li>
      <li><a href="#">PC games</a></li>
      <li><a href="#">X-Box games</a></li>
    </ul>
  </li>
</ul>




To hide the bullets in the unordered list we set the list-style-type to none and set the margin and padding to 0, relative to the menuContainer to remove any default indentation.


#menuContainer ul { 
  list-style-type: none;
  margin: 0;
  padding: 0;
}

The code below defines the look of the <li> elements and their corresponding links.


#menuContainer li {
  background-color: #CC6600;
  border: 1px solid #FF9933;
  width: 10em;
  /* this is to make the submenus position relative to this li */
  position: relative; 
}

#menuContainer a {
  text-decoration: none;
  color: #FFCC66;
  font-weight: bold;
  font-size: 10pt;
  font-family: Verdana, Arial, sans-serif;
}

Here, we are setting the background, border and width attributes for all <li> elements within the menuContainer. We are also stripping out the default underlines in links and setting our own font attributes.

When the user moves their mouse over an item, we want the background colour, border and the link colour of the item to change. We do this with:


#menuContainer li:hover {
  border: 1px solid #CC6600;   
  background-color: #FF9933;
}

#menuContainer a:hover {
  color: #993300;
}

Next, we want to position the second and third levels of the menu to the right of their parent element using absolute positioning. Note, they will be positioned, relative to their parent <li> item — we made this work using position:relative when declaring the li style above. Additionally, we want the child nodes to align with the parent node, using top:0. Finally, when we have positioned our elements to our liking, we want to hide them until the user moves their mouse over their corresponding parent items. This can be done by setting the visibility attribute to "hidden", as shown below.


#menuContainer ul ul {
  position: absolute;
  left: 10em;
  top: 0;
  visibility: hidden;
}

You may be wondering if the "#menuContainer ul ul" signifies the second level of the menu only. It may appear to be so, but the style actually applies to all lists that have a parent list element. This means that the style is applied to all the lists but the top level.

Now to the fun part! We want the second and third levels to pop out when the user moves their mouse over their parent <li> elements.


#menuContainer li:hover > ul {
  visibility: visible;
}

Notice that we are using the child selector > for this rule, which is different to the previous rule. Here, we are imposing this rule on the <ul> which is a direct descendant of a <li>. In other words, the <ul> is contained directly within the <li> and is therefore its child element. What this rule basically says is that each <li> element will display only its direct child menu elements.

This code will work beautifully in Firefox and Internet Explorer 7, but the bad news is that if you are using an older version of IE, you will encounter problems. What you will probably notice is that while moving your mouse over the first level items the background colour will not change, nor will the second and third levels of the menu be displayed. This is because older versions of IE do not support the hover property over any HTML element but links. The usage of the child selector (>) is also not supported. So, if you are determined to make this menu work in older, non standards-compliant versions of IE, you will need a workaround.




/*Mouseover: display second level or third level pop-up*/

#menuContainer li:hover ul, #menuContainer li:hover li:hover ul {
  visibility: visible;
}

 

Now, without the child selector when the user hovers over the first level items, both the second and third-level menus will get displayed.


/* Hide third level menu when the mouse is over the first level li*/
#menuContainer li:hover ul ul {
	visibility: hidden;
}


With this rule we are hiding the third-level menu when the second-level menu needs to be displayed.

The above code uses a CSS feature called specificity, which dictates what rule should be displayed when multiple rules apply for the same element. If more than one rule applies for the same element, the one selected will be the most specific one. For example, if we have two rules, "ul ul" and "li:hover ul", and they both apply to a given <ul> element, then the one that is actually used is the more specific, "li:hover ul". The "#menuContainer li:hover li:hover ul" in the code above is the more specific one and tells the third level to display when both the first and the second levels are in hover.

So that takes care of the child selector problem but what about the hovers? Peter Nederlof has made this possible with a handy whatever:hover script . This script uses an IE-specific feature to dynamically attach JavaScript events to those elements that have hover styles. To use the script, you will need to save it to a file called ccshover.htc and reference it in your stylesheet like this:


body {
  margin: 5px;
  padding: 0;
  behavior:url("csshover.htc");
}

To make the menu work across all browsers, you can reference two stylesheets in your HTML page using IE conditional comments.


<!—[if (gte IE 7)|(!IE)]> <!—>
<link rel="stylesheet" type="text/css" href="menu.css" />
<!—<![endif]—>

<!—[if lt IE 7]>
<link rel="stylesheet" type="text/css" href="menuie6.css" />
<![endif]—>

Now you're all set to use your menu and be confident it will work in any browser. Well, almost. If you've noticed, the menu colours are inverted in Opera and Safari browsers, but I'll leave that to you to look into as a final challenge.

Below are the full working code examples we've used in this tutorial:

HTML | Default CSS stylesheet | IE 6 and below CSS stylesheet

Editor's Picks

Free Newsletters, In your Inbox