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">▶</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">▶</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 ("▶" 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.
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.
All of these problems are fixed with some JavaScript code, using the
menuInit()
function mentioned earlier. We'll look at this
function next.