DHTML Lab: Hierarchical Menus, I; Menu Hiding | WebReference

DHTML Lab: Hierarchical Menus, I; Menu Hiding


Logo

DHTML Hierarchical Menus, Part I
hiding part or all of the menu tree

Webreference

Contents

Open the menus above to remind yourself that this is a technique worth learning.

The complete hiding of a menu tree is handled by five functions, which are also methods of menu elements: hideTree(), hideChildren(), hideParents(), hideTop() and hideSelf().

Hiding the Complete Menu Tree

As we saw on the previous page, a menu's hideTree method is invoked, when an item's mouseout handler determines that the user is leaving the menu. Also, recall that the allTimer variable is assigned delayed execution of hideTree.

function hideTree(){
  allTimer = null;
  if (isOverMenu) return;
  if (this.hasChildVisible) {
    this.hideChildren();
  }
  this.hideParents();
}

As soon as hideTree() is called, the allTimer variable is nulled, because the timer has fired. Next we evaluate isOverMenu, and return from the function if it is true. If during the 10 milliseconds delay before execution of hideTree, the user moused over a child or parent menu, the isOverMenu variable will have been set to true. Therefore no further action is taken, the tree remains visible, and the user coninues menu navigation.

If the user has left the menu tree, we determine if the menu moused out of has a child menu visible (hasChildVisible). If it does, the hideChildren method is used. Whether a child menu is visible or not, a parent will probably be, so finally hideParents is called.

Hiding Child Menus

The hideChildren() method is called from three other methods, as we have seen: itemOver(), hideAll(), and hideTree(). In the first case, the item is passed as an argument. The other call it with no argument. If an argument is passed, the function calls it item.

First, hideChildren() determines if the child menu of the calling menu, has its own child menu visible. This statement will only evaluate to true when the function is called from hideAll() and the user is moving the mouse at light speed. It may happen, so we hide this child menu first.

function hideChildren(item) {
  if (this.visibleChild.hasChildVisible) {
    this.visibleChild.visibleChild.showIt(false);
    this.visibleChild.hasChildVisible = false;
  }
  if (!this.isOn || !item.hasMore || (keep with next)
      this.visibleChild != this.child) {
    this.visibleChild.showIt(false);
    this.hasChildVisible = false;
  }
}

Next, the function evaluates three different expressions. If any one of them is true, the remaining statements are executed:

!this.isOn - If the calling menu has been moused out of, its isOn property will be false, making this statement true. This statement will always evaluate to true when hideChildren() is called from hideAll() or hideTree().

!item.hasMore - When hideChildren() is called from itemOver(), the mouse is over the calling menu and its isOn property is true. Therefore, this second statement is evaluated. If the item that the mouse is over does not have a child menu, then the item's hasMore property is false and the statement is true.

this.visibleChild != this.child - If the item does have a child menu, the third statement is evaluated. If the visible child menu is not this item's child menu then this last statement evaluates to true.

In plain English, the above would read: Hide the child menu if the mouse is off its parent menu completely. If the mouse is still on the parent menu, don't hide the menu if it is the child of the item the mouse is on.

We have delved into such detail for hideChildren() because it is the function that controls child visibility when we navigate up the menu tree. That is, when we leave a child menu and move back to its parent. We must avoid blinking of menus, bad transitions, and so on.

Hiding Parent Menus

The hideParents() method is only called from hideTree(), making it much simpler. It hides all parent menus except for the top level menu. The top level menu is assigned to the local variable whichEl, and has its hideTop() method called.

function hideParents() {
  if (this.hasParent) {
    this.showIt(false);
    if (this.parentMenu.hasParent) {
      this.parentMenu.isOn = false;    
      this.parentMenu.showIt(false);
      this.parentMenu.parentMenu.isOn = false;
      whichEl = this.parentMenu.parentMenu
    }
    else {
      this.parentMenu.isOn = false;
      whichEl = this.parentMenu;
    }
  }
  else {
    whichEl = this;
  }
  whichEl.hideTop();
}

If the calling menu has a parent (hasParent), it is not a top level menu, so it is hidden immediately.

If its parent has a parent, then the grandparent must be the top-level menu, so the parent is hidden and the grandparent is assigned to whichEl.

If its parent does not have a parent, then the parent is the top level menu, and is assigned to whichEl.

If the calling menu does not have a parent, then it itself is the top level menu so it becomes whichEl.

In short, hideParents() hides all parent menus in a tree and sets their isOn property to false, except for the top level menu in the tree, which has its hideTop() method invoked.

Hiding the Top Level Menu

The top level menu is special. We don't want it to be hidden immediately. This allows users to move back on and continue navigation in case the mouse slips. It also allows for more elegant behaviour. The hideTop() method is called from both hideParents() and popdown(), with the same results:

function hideTop() {
  whichEl = this;
  this.hideTimer = (keep with next)
    setTimeout("whichEl.hideSelf()",mSecsVis);
}

We assign the calling top level menu to a local variable, whichEl, and use the setTimout() method to call the menu's hideSelf() method after the time interval stored in mSecsVis, our global variable. This delayed execution is assigned to the menu's hideTimer property.

If the user mouses back over the menu before the setTimout() fires, the menuOver() method will clear the hideTimer variable, cancelling the execution of hideSelf().

If the user stays off the menu, hideSelf() will execute:

function hideSelf() {
  this.hideTimer = null;
  if (!this.isOn && !isOverMenu) { 
    this.showIt(false);
  }
}

The timer has fired, so it is nulled. A final check that the user has not moved back on is performed by checking the isOn property and the isOverMenu variable, and the menu is finally hidden.

That about wraps this routine up. Next page, we'll have final comments, before moving on to the code pages.


Produced by Peter Belesis and

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

URL: http://www.webreference.com/dhtml/column14/menuHide.html