DHTML Lab: Cross-Browser Hierarchical Menus; Explorer Events | WebReference

DHTML Lab: Cross-Browser Hierarchical Menus; Explorer Events


Logo

Cross-Browser Hierarchical Menus
filtering elements from event firing

Webreference

Contents

FAIR Trick Question:
How do the above menus differ from all the others so far.
(in IE4, that is)

Experts

Clear

Events, Events, Everywhere

Our single item example menu in the left column now has the triangle image added. Pass your mouse over the menu, making sure you pass over the image. What are the results?

Now open the regular popup menus to the left. Pass over the image. What happens to the item highlight? In the last two pages, we have used a different script than our final one. One that omits the statements that we will discuss and add on this page. On the previous page, the child menu overlap covered the image, ergo the "unfair" trick question.

Using what we learned on the previous page, we know that:

  1. the menu onmouseover/onmouseout event handlers are also assigned to the item and the image, the two elements contained within the menu element;
  2. the item onmouseover/onmouseout event handlers are also assigned to the image, the single element contained within the item element;
  3. the menu element has one event handler statement (function) to execute: its own;
  4. the item element has two event handler statements to execute: its own and the menu's;
  5. the image element also has two event handler statements to execute: the item's and the menu's, since no event handler has been specifically defined for the image;
  6. the item and the menu occupy the exact same physical space, so only the item's event handlers will fire;

Point 5, above, demonstrates another important difference between Navigator and Explorer. Navigator considers the document elements in mathematical abstraction. It knows the menu element is there, and that it is enclosing the item, making it 'larger,' even though it occupies the same space as the item. In the same way, we spend hours discussing no-dimensional "points" and one-dimensional "lines" in math class, even though they are invisible. As authors, we know the menu element is there. We put it there!

Explorer considers only the elements that occupy the page's two dimensional space. This ability to overlook elements is compensated for by the transference of event handling to contained elements. Scripted correctly, the event handlers can still handle statements meant for the overlooked outer element, as demonstrated by the functions on the previous page.

This two dimensional approach is also illustrated by the firing of the item's onmouseout when we mouse onto the image. Why does it fire? We are not leaving the item. We are still on it. If we are over more than one element simultaneously, (eg. menu/item/image) the one lowest in the hierarchy is the source, or current, element. Explorer considers us to have "left" the enclosing element, to have moused out of it.

If not understood and scripted correctly, this behaviour could be disasterous. When we mouse over the image, we are first mousing out of the item which calls itemOut(), and menuOut(). Then we mouse over the image, which calls itemOver(), and menuOver(). This leads to the background color "blinking", and if we did not use timers for menu hiding, it would cause the collapse of the menu tree, as well.

New Properties to the Rescue...

Luckily, Explorer provides two new properties of the event object, fromElement and toElement, to assist us with mouseover and mouseout event handling.

On a mouseover or a mouseout, event.fromElement is the element we are coming from, and event.toElement is the element we are moving to.

Do not confuse either of these properties with event.srcElement, which stores the element that fired the event handler. Given Explorer's generous distribution of event handling, the source element could be a dozen or more elements removed from the actual elements involved in the mouse action.

...and a New Method

Explorer 4 has also introduced a powerful method: contains(), for use with conditional statements. The syntax is:

BooleanVariable = containingElement.contains(childElement);
or:
if (containingElement.contains(childElement)) {
    statements to execute
}

Needless to say, it checks whether one element is contained within another, returning true if it is, false if it isn't.

Let's modify the mouse event functions to avoid unwanted behaviour, using the new properties, the new method, and our old friend srcElement.

The OVER Functions

When we mouse over an image, we do not want the itemOver() function called superfluously, so we exclude the image from executing the function by including this statement as the first line:

if (IE4 && event.srcElement.tagName == "IMG") return;

The above reads: If this is Explorer and the event was generated by an image, return from the function. Our itemOver() function becomes:

function itemOver(){
 if (IE4 && event.srcElement.tagName == "IMG") return;
 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);
 }
}

The menuOver() function call from the image is harmless, so that function remains the same.

function menuOver() {
 this.isOn = true;
 isOverMenu = true;
 currentMenu = this;
 if (this.hideTimer) clearTimeout(this.hideTimer);
}

The OUT Functions

The only case where the menuOut() should not be allowed to fire is when we mouse from an item to a contained image. In otherwords, when the source element contains the to element.

function menuOut() {
if (IE4 && event.srcElement.contains(event.toElement)) return;
 this.isOn = false;
 isOverMenu = false;
 if (IE4) allTimer = setTimeout("currentMenu.hideTree()",10);
}

Finally, we update itemOut(), the function responsible for the background color flickering.

We don't want itemOut() to be executed in these cases:

  1. we are moving from an item to its contained image;
  2. we are moving from an image to its containing item;
function itemOut(){
if (IE4 && (event.srcElement.contains(event.toElement)
 || (event.fromElement.tagName=="IMG" &&  (keep with next line)
     event.toElement.contains(event.fromElement))))
   return;
  if (NS4) {
  this.bgColor = backCol;
  if (!isOverMenu) {
   allTimer = setTimeout("currentMenu.hideTree()",10);
  }
 }
 else {
  this.style.backgroundColor = backCol;
  this.style.color = fntCol;
 }
}

We have now included the minimum checks necessary to avoid unwanted menu behaviour, and learned a bit about Explorer events along the way, hopefully. Let's tidy up loose ends with a refresher of the hide routines.


Produced by Peter Belesis and

All Rights Reserved. Legal Notices.
Created: Feb. 27, 1998
Revised: Feb. 27, 1998

URL: http://www.webreference.com/dhtml/column15/menu2IEvents.html