-->
Bookmark and Share

Revenge of the Menu Bar

See the demo page for the finished version of the code.

The Document onmousedown Event

To get this effect while preventing interference with any mousedown events for other elements on the page, an event listener is used to capture the mousedown event for the document object.

This bit of code near the start of the script sets the pageMousedown() function as the event handler.

// Capture mouse clicks on the page so any active button can be
// deactivated.

if (browser.isIE)
  document.onmousedown = pageMousedown;
else
  document.addEventListener("mousedown", pageMousedown, true);

The function itself looks like this.

function pageMousedown(event) {

  var el;

  // If there is no active button, exit.

  if (activeButton == null)
    return;

  // Find the element that was clicked on.

  if (browser.isIE)
    el = window.event.srcElement;
  else
    el = (event.target.tagName ? event.target : event.target.parentNode);

  // If the active button was clicked on, exit.

  if (el == activeButton)
    return;

  // If the element is not part of a menu, reset and clear the active
  // button.

  if (getContainerWith(el, "DIV", "menu") == null) {
    resetButton(activeButton);
    activeButton = null;
  }
}

If there is no button currently active, there's nothing to do so it simply exits.

But if there is, it first determines which element was clicked on. If that happened to be the active button, it again exits. The button's onclick event handler will take care of it.

If not, it then determines if the element being clicked on is part of any menu, using the getContainerWith() function. If no such element is found, the mouse must have been clicked somewhere outside of any menu. So the function can deactivate the currently active button.

You can try this below. Activate a button then click somewhere on the page, off of the drop down menu. The active button will then be reset.

With this addition, we have a fully functioning menu bar. But to make it even more useful, we'll add support for sub menus.

Sub Menus

To do this, we'll add some more style classes and elements to the existing menu display. First, the CSS:

div.menu a.menuItem span.menuItemText {}

div.menu a.menuItem span.menuItemArrow {
  margin-right: -.75em;
}

For menu items that have a sub menu, we change the HTML to look like this:

<div id="menu3" class="menu">
<a class="menuItem" href="...">Menu 3 Item 1</a>
<a class="menuItem" href="...">Menu 3 Item 2</a>
<a class="menuItem" href="...">Menu 3 Item 3</a>
<a class="menuItem" href="...">
  <span class="menuItemText">Menu 3 Item 4</span>
  <span class="menuItemArrow">&#9654;</span></a>
<a class="menuItem" href="...">Menu 3 Item 5</a>
<a class="menuItem" href="...">
  <span class="menuItemText">Menu 3 Item 6</span>
  <span class="menuItemArrow">&#9654;</span>
</a>
</div>

Items with no sub menu are defined as before, using plain text within the A tag. For items that will have a sub menu, we break the link text up in two separate SPAN tags. Note that while the menuItemText class has no specific style rules, its use will help in identifying the SPAN element for the item text within the code.

The first SPAN, with style class menuItemText, contains the item text while the second, with style class menuItemArrow, contains an arrow character ("&#9654;" refers to the Unicode "black right-pointing triangle" character, but see the note on IE below).

A sample is shown at left.

There are several advantages to using a text character for the arrow instead of an image. For one, the :hover pseudo-class takes care of changing the arrow color automatically. You don't need to create separate images and code to swap them on mouseover.

Second, you can change the font size or colors on the style classes for the menu items and the arrow character will change along with it. This eliminates the need create new images anytime you want to change the menu appearance.

Browser Compatibility

In some cases, Internet Explorer does not properly display that Unicode character, which you may see in the sample above if you are viewing this page with IE (IE may display it as either a small, hollow square or as an overly large arrow).

Opera has a bug that causes the arrow to disappear due to the negative value on the margin style. If viewing this page in Opera, you won't see the arrows.

Both of these problems will be addressed shortly.

This set up works well, until you try using menu items with varying amounts of text. Take a look at the following example:

Notice that the arrows appear just after the text (if your browser displays them at all). We'd rather have them appear flushed with the right hand side of the menu.

Browser Compatibility

Also note that, in IE, the hover effect does not work if the mouse pointer is over a blank area of an item instead of directly over the text. You can see this by moving your mouse over the sample menu above, if you're viewing this page with IE. This seems to be a bug with that browser and it occurs whether or not a menu has any items set up for sub menus.

All of these problems are fixed with some JavaScript code, using the menuInit() function mentioned earlier. We'll look at this function next.