DHTML Lab: Cross-Browser Hierarchical Menus; Menu Navigation
Cross-Browser Hierarchical Menus
| |||
UNFAIR Trick Question: Experts Clear |
The Explorer Event ModelThe Explorer 4 event model, as we all know by now, has very little in common with the Navigator equivalent. Yes, there have been attempts to create cross-browser versions, the most notable being those of Netscape's Danny Goodman and our own Doc JavaScript. These attempts succeed when the concern is simple event capturing or identification. In other cases, we have seen simplifications like:
The above statement is true, to a point. For simple applications, it suffices and is a good illustration. For more complex applications, it is misleading and can lead to unfocussed attempts at solutions. How we view and use the similarities and differences between the two event models must be on a per application basis. We must approach each problem separately, since there will be different solutions for each problem, no matter how ostensibly similar the problems may appear. For example, all our columns, except for column 5, have used the event model in some form, but with different approaches. The present application presents yet another, more complex, unique problem. How complex is it? Well, not very, but more than usual. But it must be mentioned that in the Microsoft SDK, there is an excellent chapter on the Explorer event model: Understanding the Event Model. Most problems are given in-page solutions. Some are illustrated after clicking Show Me buttons. Under the heading More About Event Bubbling, a particular problem is outlined: a menu DIV that should respond to onmouseover and onmouseout that contains menu items that should be highlighted/unhighlighted in response to their own onmouseovers and onmouseouts. Sound familiar? Of course. It is our menu problem. What solid in-page solution is given? Er...none. Several possible solutions are alluded to, but no code is provided, and the beginner would probably be baffled. I sympathize with the author, for the reason given above: Different applications have different solutions. If Microsoft had provided a coded solution, it would not have been enough for us, because we have an image in our menu items! You'll understand the implications of this on the next page. We'll provide a solution here, of course, but it will be specific to our application. If you want to learn more about the Explorer event model, the above SDK chapter is recommended, as is Doc JavaScript's exploration. We'll allude to Explorer event model specifics only when necessary, as we progress. Our philosophy will be:
Mousing OVER an ItemIf we follow the simplification, the item's mouseover fires first, calling itemOver(). The itemOver() function is structurally the same as before. The item's background color is changed, as is the font color for Explorer. If any child elements of the containing menu are visible, the menu's hideChildren() method is called. If the item opens a child menu, then that menu is positioned and made visible, following the Navigator logic. function itemOver(){ if (NS4) { this.bgColor = overCol; } else { this.style.backgroundColor = overCol; this.style.color = overFnt; } if (this.container.hasChildVisible) { this.container.hideChildren(this); } if(this.hasMore) { if (NS4) { this.childY = this.pageY + childOffset; this.childX = (keep with next line) this.container.left + (menuWidth - childOverlap); } else { this.childY = (keep with next line) this.style.pixelTop + this.container.style.pixelTop + childOffset; this.childX = (keep with next line) this.container.style.pixelLeft + (menuWidth - childOverlap); } this.child.moveTo(this.childX,this.childY); this.child.keepInWindow(); this.container.hasChildVisible = true; this.container.visibleChild = this.child; this.child.showIt(true); } } Mousing OVER a MenuOur menuOver() function remains exactly the same. That is, variables are set and the menu's hide timer is cleared if it happens to be running. function menuOver() { this.isOn = true; isOverMenu = true; currentMenu = this; if (this.hideTimer) clearTimeout(this.hideTimer); } Mousing OUT of an ItemRecall that, in Navigator, the mouseover for the menu fired before the mouseover for the item, so it was up to the item to hide the menu. Following the logic of the oversimplification of the event hierarchy stated above, we assume that the item's mouseover will preceed the menu's mouseover. In that case, itemOut() should not worry about hiding the menu. It should only change the font and background colors of the item back to the default ones: function itemOut(){ if (NS4) { this.bgColor = backCol; if (!isOverMenu) { allTimer = setTimeout("currentMenu.hideTree()",10); } } else { this.style.backgroundColor = backCol; this.style.color = fntCol; } } Mousing OUT of a MenuFinally, menuOut() should not only set variables, as in Navigator, it should also start the timer that will hide the menu: function menuOut() { this.isOn = false; isOverMenu = false; if (IE4) allTimer = setTimeout("currentMenu.hideTree()",10); } That Was Easy! Why the Fuss?We've illustrated, in the functions above, how to use the simplification to our advantage. This is the case where "it suffices." Looking over the functions, and the discussion of them, it would follow that the sequence is the following:
Right? WRONG! If you are using Explorer 4, there is a live illustration of the event firing in the left column. A menu contains a single item: Experts. The menu is called egMenu and the item, egItem. They have the same event handlers and function calls as our popup menus. Pass your mouse over this menu/item. In the left column will appear the name of the element that fires the event and the function that was called. If you want to clear the display and start the trial again, click Clear. Try it. What do you notice? The functions are called in the expected order, but always by the "item." Never by the "menu." What's going on here? When we assigned the mouseover/mouseout event handlers to the menu element, we assigned those same event handlers to all elements contained within the menu. In the left column example, there is one contained element, the item. When we assigned the mouseover/mouseout event handlers to the item, we also assigned them to every element that might be contained by the item. In this case, none. Therefore, a menu calls one function when its events fire; an item calls two functions when its events fire. In Explorer, if the boundaries of two elements are exactly the same, the element lower in the hierarchy hides the higher element. The higher element's mouseover/mouseout events, which are boundary-specific, do not fire! In Explorer, therefore, the menu handlers and functions could have been absorbed by the item handlers and functions. Why is the behaviour correct, if the item is the only element calling the functions? Because the functions are methods of the menu so the statements within the functions apply only to the menu. If, instead of making the functions methods of the menu, we used the event.srcElement property, we would be acting on the item, with unpredictable results. In conclusion, and hopefully not in confusion, we defined our functions as if both the menu's and the item's events would fire. They are cleaner and cross-browser-adaptable. And they work! Well, almost. We have still not answered the trick question above. We'll do that on the next page. |
Produced by Peter Belesis and
All Rights Reserved. Legal Notices.Created: Feb. 27, 1998
Revised: Feb. 27, 1998
URL: https://www.webreference.com/dhtml/column15/menu2Over.html