Adding the horizontal menu to the page

Start by taking the basic page and stripping some of the extraneous stuff out of it.

  • Remove the place-holder paragraph containing "<p>contentL</p>"
  • Get rid of all the content describing the layout of the <div> tags on the page, and replace it with something simple.  It's good to have some content to help identify problems that arise, especially those as a result of floats—more on that later.
  • Add the actual menu in place of the place-holder paragraph containing "<p>Horizontal Menu Bar</p>"

Menu Structure

The basic menu structure is really quite simple.  In the <div id="navHd"> tag, create an unordered list <ul> tag that contains a list of top level menu items in <li> tags.  Inside the <li> tags, use <a> tags to provide hyperlinks to jump to a target.  For example, the top level menu might be coded this way:

  1. <ul>
  2.   <li><a href="some link">Top item 1 text</a></li> 
  3.   <li><a href="some link">Top item 2 text</a></li> 
  4.   <li><a href="some link">Top item 3 text</a></li> 
  5.   <li><a href="some link">Top item 4 text</a></li> 
  6. </ul>

A submenu is added to top-level items by inserting a similar structure inside an <li> tag.  For example:

  1. <ul>
  2.   <li><a href="some link">Top item 1 text</a></li> 
  3.   <li><a href="some link">Top item 2 text</a>
  4.     <ul>
  5.       <li><a href="some link">Sub item 2.1 text</a></li> 
  6.       <li><a href="some link">Sub item 2.2 text</a>
  7.       </li> 
  8.       <li><a href="some link">Sub item 2.3 text</a></li> 
  9.       <li><a href="some link">Sub item 2.4 text</a></li> 
  10.     </ul>
  11.   </li> 
  12.   <li><a href="some link">Top item 3 text</a></li> 
  13.   <li><a href="some link">Top item 4 text</a></li> 
  14. </ul>

And submenus can be added within submenus, ad infinitum.  Just be certain each submenu contains a complete <ul><li></li> . . . <li></li></ul> block.  See the results here: example page.  Ya, that's really ugly!  Be sure to right-click and view the source code carefully.

Floating the top level menu & hiding submenus

At this stage the example page is not very pretty and has several things wrong with it, the most obvious being that the top-level menu items should be on one line extending horizontally across the menu bar, and all submenus should be hidden.  To accomplish that, create the class ".navH" within the "#navHd" <div> definitions, and add the following style rules to the document header:

  1. #navHd.navH, #navHd.navH ul {
      padding: 0; margin: 0;
      list-style: none;
    }
  2. #navHd.navH a {
      display: block;
      width: auto;
      white-space:nowrap;
    }
  3. #navHd.navH li {
      float: left;
      width: auto;
    }
  4. #navHd.navH li li {
      float:none;
      position:relative;
    }
  5. #navHd.navH li ul {
      position: absolute;
      width: auto;
      display: none;
    }
  6. #navHd.navH li {
      padding: 0 10px;
    }

The first rule removes the bullets and indents that are the default for <ul> elements.

The second displays the <a> elements without text wrapping, keeping each on a single line.

The third floats all the <li> elements so they line up on the same line with varying widths.

The fourth prevents submenu items from inheriting the float behavior of the main items.  They should line up one on top of the other, like multiple paragraphs in a block, not floating one after the other on the same line.

The fifth rule hides the submenus.  It also instructs the browser to use absolute positioning for submenus, taking them out of the normal flow of the document, which will be necessary later when they are displayed.

The last rule above is not strictly necessary at this stage.  It is simply convenient formatting for visual effect, adding some space between the menu items.  Without it the menus will work fine, but they would all crowd together, and it would be difficult to distinguish one item from the next.  This will be removed or overridden when it's time to format the menu.

See the results here: example page.

ID vs. Class: This could be done as easily with simple id selectors, or simple class selectors.  However, id selectors offer greater specificity, which allows for greater control and eliminates the difficulties that can arise with inheritance conflicts.  But a class selector provides the ability to add or subtract groups of rules with a single class reference in a tag.  Combining the two in this way produces the greater specificity of an id selector with the ability remove a group of rules.  This will be critically important when the menus are animated.

Clearing floats

There's a problem in the most recent example that might not be readily apparent. Go back to it and look at the background color of the menu bar: it's gray.  The most recent example contains a link to the previous example in its content, making it possible to switch back and forth between the two.  Go back to it again and do that.

The background color of the menu bar was originally, and is still, pink.  And yet, in the most recent example it appears to be gray.  Furthermore, the text "Vertical Menu Bar" is now at the end of the horizontal menu bar.

The problem arose because, in the third CSS rule above, when the <li> elements were floated, that took them out of the normal flow of elements on the page.  Here are a couple of web sites that explain the problem of clearing floats in considerable detail, and provide several solutions: wiki-Clearing Space and Perishable Press.

In these examples the "clearfix" method is employed as described near the end of Perishable Press.  Add the following class and IE conditional comment to the <head> of the document:

  1. .clearfix:after {
      content: ".";
      display: block;
      height: 0;
      clear: both;
      visibility: hidden;
    }
  2. <!--[if lte IE 7]>
      <style type="text/css" media="screen">
        .clearfix {zoom: 1;}
      </style>
    <![endif]-->

Note that:

  • IE/Mac support is not included
  • The .clearfix class definition is placed inside <style> tags in the <head>
  • The IE Conditional Comment is placed in the <head>, but not inside <style> tags.

To make use of this, add a class reference to the navHd <div> so it now reads <div id="navHd" class="navH clearfix">.  Problem solved!  See the result here: example page.

Finally: displaying submenus

To provide real functionality so that submenus are displayed when the cursor hovers over a parent menu, the following CSS rules are added:

  1. #navHd.navH li li ul {
      top:0;
      left:100%;
    }
  2. #navHd.navH li:hover li ul, /* 2nd shown, hides 3rd level */
    #navHd.navH li:hover li li ul, /* hides 4th level */
    #navHd.navH li:hover li li li ul { /* hides 5th level */
      display:none;
    }
  3. #navHd.navH li:hover ul, /* Display's 2nd level */
    #navHd.navH li li:hover ul, /* Display's 3rd level */
    #navHd.navH li li li:hover ul, /* Display's 4th level */
    #navHd.navH li li li li:hover ul { /* Display's 5th level */
      display:block;
    }

The 2nd level <ul> submenus, those immediately beneath the top level items, will drop down by default.  But 3rd and higher level menus need to fly to the right.  That's the purpose of the first new rule above.  It instructs all submenus at the 3rd level or higher to align the top of the submenu's <ul> block with the top of its parent menu item, and position its left side to the right by the width of the parent.

The second and third rule groups above are a little complicated and require careful explanation.  The li:hover ul rule in the 3rd group displays the 2nd level menu, but alone it would apply to all descendent <ul> blocks, and the entire menu tree for that submenu would appear.  So without something to specify the behavior of deeper level menus, all of the submenus would be displayed at once.  The li:hover li ul rule in the 2nd group presets that behavior, insuring that all 3rd and higher level menus are hidden when the 2nd level is displayed.

To display the 3rd level menu when the mouse hovers over an item in the 2nd level, the 2nd rule in the 3rd group overrides the 1st rule in the 2nd group, but only for a hover event.  The 2nd rule in the 2nd group now hides all 4th and higher level menus.

Note that the rules in the 2nd group set conditions for keeping higher level submenus hidden, while each rule in the 3rd group overrides a rule in the 2nd group.  Because of this override behavior, the 3rd group that displays submenus, must always follow the 2nd group that hides submenus.

The above rules will support five menu levels.  To display a sixth, or more, just extend the second and third rule groups by adding a new rule for each level to both groups.

Also note that a background color and left border was added to the <li> formatting:

  1. #navHd.navH li {
      padding: 0 10px;
      background-color: #fcf;
      border-left: 1px black solid;
    }

Just like before, this formatting is not necessary for the menus to function properly.  But without it, the menu functionality is difficult to observe.  See the result here: example page.

Aligning the submenus

When all of this is done, it becomes obvious that the 2nd level submenu appears with its left edge beneath and adjacent to the left edge of the <a> element in the parent <li>, which means it's indented to the right by the sum of the parent <li>'s left padding and border.  It's not necessary to do this, but if one wants the left edges of the parent and child to line up, add negative left margin to the child <ul> element equal to the sum of the parent <li>'s left padding and border:

  1. #navHd.navH li ul {
      margin-left:-11px;
    }
  2. #navHd.navH li li ul {
      margin-left:0;
    }

Note that the first rule above adds the negative margin to all descendent <ul> elements.  The second rule is necessary to remove that rule from all but the 2nd level (first dropdown) submenu.  See the result here: example page.

Also note that the tops of deeper submenus, which fly right, are offset from the top of the parent <li> by the parent <li>'s top border.  But since there is merely a 1 pixel top border, the offset is only 1 pixel.  One can use similar rules to align the tops of submenus with their parent <li>, though that hasn't been done here.  In any case, some experimentation is always required.

Next

At this stage, the multi-level dropdown and flyright horizontal CSS menu bar is complete and fully functional for all the supported browsers.  And it offers the advantage that all submenus automatically adjust width to accommodate the content of the widest contained item.  To see this in action, go to the example page and navigate to Item 3.2.3.

There's one gotcha here: IE 6 & 7 exhibit some very non-compliant behavior that can make these menus unusable.  At the time of this writing more than 5% of users are still using IE 7, and more than 20% are using a mix of browsers older than that.  But the IE 6 & 7 hacks required to correct these problems work well and don't degrade performance.  So it behooves all developers to continue support for these ubiquitous browsers.  Go to the next tutorial to find out how.

Horizontal:  H-1  [H-2H-3  H-4  H-5  next