Revenge of the Menu Bar
See the demo page for the finished version of the code.Positioning a Sub Menu
As with positioning a menu under a button, we first find the coordinates of the link element with respect to the page. Adding the width of the item to the horizontal coordinate will put the sub menu just to the right of the item.
// Get position for submenu based on the menu item. x = getPageOffsetLeft(item) + item.offsetWidth; y = getPageOffsetTop(item);
Unlike the buttons and main menus however, we take the sub menu positioning a bit further. With a long chain of sub menus, each positioned downward and to the right of the previous one, it's easy to run out of room. A Sub menu may end up extending past the edge of the browser window.
To prevent this, we find the coordinates of the current, viewable area of the page using the browser's window dimensions and adjusting for any scrolling offsets.
// Adjust position to fit in view. var maxX, maxY; if (browser.isIE) { maxX = (document.documentElement.scrollLeft != 0 ? document.documentElement.scrollLeft : document.body.scrollLeft) + (document.documentElement.clientWidth != 0 ? document.documentElement.clientWidth : document.body.clientWidth); maxY = (document.documentElement.scrollTop != 0 ? document.documentElement.scrollTop : document.body.scrollTop) + (document.documentElement.clientHeight != 0 ? document.documentElement.clientHeight : document.body.clientHeight); } if (browser.isOP) { maxX = document.documentElement.scrollLeft + window.innerWidth; maxY = document.documentElement.scrollTop + window.innerHeight; } if (browser.isNS) { maxX = window.scrollX + window.innerWidth; maxY = window.scrollY + window.innerHeight; } maxX -= item.subMenu.offsetWidth; maxY -= item.subMenu.offsetHeight; if (x > maxX) x = Math.max(0, x - item.offsetWidth - item.subMenu.offsetWidth + (menu.offsetWidth - item.offsetWidth)); y = Math.max(0, Math.min(y, maxY));
Taking into account the width and height of the sub menu, we can define a range of coordinates where the sub menu can be positioned while staying fully in view.
Should our original position fall outside this range, we adjust it to place the sub menu to the left of its parent menu and/or move it upwards so that its bottom edge will remain in view.
document.body
object. But IE 6 in standard mode
stores these values in the document.documentElement
object
instead. The code above handles this by taking the values from
document.documentElement
if they are not set to zero. Otherwise,
it takes them from document.body
.
Once a suitable position is found, the sub menu is moved and made visible.
// Position and show it. item.subMenu.style.left = x + "px"; item.subMenu.style.top = y + "px"; item.subMenu.style.visibility = "visible";
The last step in this function cancels the event bubble.
// Stop the event from bubbling. if (browser.isIE) window.event.cancelBubble = true; else event.stopPropagation();
Since the item is part of the menu DIV, and the menu DIV also has an
onmousemove
event handler defined for it, any
mousemove
event fired on the item also fires for its parent
menu.
That's not good because, as we'll see next, the onmousemove
handler for the menu closes any active sub menu. If the event were allowed to
bubble from here, the sub menu just displayed would be immediately hidden
again.
Deactivating Sub Menus
The sample menu below demonstrates the code up to this point. Mousing over an item with a sub menu activates that item and sub menu. It will stay active until you mouse over some other sub menued item.
This presents a problem because once any sub menu is opened, you can't get rid of them. Moving the mouse to another, normal item does nothing while moving to another sub menued item just switches the active sub menu. (For this sample, you can use the link above to get rid of the sub menus).
Instead, we want the active sub menu to close when any other item in the
parent menu is moused over. That's where the mouseover
event
handler on the menu DIV comes in.