DHTML Lab: Hierarchical Menus Version 3 | 8 | WebReference

DHTML Lab: Hierarchical Menus Version 3 | 8


Logo

Hierarchical Menus: Version 3
item mouse events

WebReference

Click the link above to reveal menu. Click anywhere on the page to hide menu.

Parameters used for the menus on this page:

menuVersion = 3;
menuWidth = 120;
childOffset = 5;
perCentOver = 30;
secondsVisible = 1;
fntCol = "black";
fntSiz = "10";
fntBold = true;
fntItal = false;
fntFam = "serif";
backCol = "#00cc00";
overCol = "#008800";
overFnt = "red";
borWid = 2;
borCol = "darkgreen";
borSty = "solid";
itemPad = 3;
imgSrc = "tri.gif";
imgSiz = 10;
separator = 1;
separatorCol =
   "darkgreen";
isFrames = false;
keepHilite = true; 
NSfontOver = false;
clickStart = true;
clickKill = true;
showVisited = "";

Background Reading:

itemOver():
  column 14
  column 15
  column 18
  column 20

itemOut():
  column 14
  column 15
  column 18
  column 20

In script listings, cross-browser code is blue, Navigator-specific code is red, and Explorer code is green.

The [cc] symbol denotes code continuation. The code is part of the preceding line. It is placed on a new line for column formatting considerations only.

The two features introduced in version 3 that help alter the itemOver() and itemOut() functions so drastically are:

  1. maintenance of item highlight across menu tree (keepHilite)

    We must track the "current item" of every menu in the tree. Mousing out of a "current item" does not mean that the item should lose the highlight, since we may be mousing to a child menu. An item should lose highlight features only if we mouse over another item in the same container menu, making this new item, the "current item." Therefore, we have to account for two diametrically opposed strands of logic:

    1. if keepHilite is false, we highlight an item on a mouseover and de-highlight it on a mouseout. This is the behaviour in previous versions.

    2. if keepHilite is true, we highlight an item on a mouseover, and de-highlight the previous "current item.". Both actions are performed together. We do not change anything on a mouseout.

  2. font color change upon item mouseover for Navigator (NSfontOver).

    To effect our "workaround" for color change, we must actually update the contents of an item element, instead of simply changing a property value.

Item mouseover

itemOver() now looks like this:

function itemOver(){
  if (keepHilite) {
    if (this.container.currentItem && this.container.currentItem != this) {
      if (NS4) {
        this.container.currentItem.bgColor = this.container.menuBGColor;
        if (NSfontOver) {
          with (this.container.currentItem.document) {
            linkColor = this.container.menuFontColor;
            write(this.container.currentItem.htmStr)
            close();
          }
        }
      }
      else {
        with (this.container.currentItem.style) {
          backgroundColor = this.container.menuBGColor;
          color = this.container.menuFontColor;
        }
      }
    }
  }
  if (IE4) {
    theEvent = menuLoc.event;
    if (theEvent.srcElement.tagName == "IMG") return;
    this.style.backgroundColor = this.container.menuBGOver;
    this.style.color = this.container.menuFontOver;
  }
  else {
    this.bgColor = this.container.menuBGOver;
    if (NSfontOver) {
      this.document.write(this.htmStrOver);
      this.document.close();
    }
  }
  this.container.currentItem = this;
  if (this.container.hasChildVisible) {
    this.container.hideChildren(this);
  }
  if (this.hasMore) {
    horOffset = (isRight) ?
[cc]   (this.container.childOverlap - this.container.menuWidth) :
[cc]   (this.container.menuWidth - this.container.childOverlap);
    if (NS4) {
      this.childX = this.container.left + horOffset;
      this.childY = this.pageY + childOffset;
    }
    else {
      this.childX = this.container.style.pixelLeft + horOffset;
      this.childY = this.offsetTop +
[cc]   this.container.style.pixelTop + childOffset;
    }
    this.child.moveTo(this.childX,this.childY);
    this.child.keepInWindow();
    this.container.hasChildVisible = true;
    this.container.visibleChild = this.child;
    this.child.showIt(true);
  }
}

The first group of statements are executed only if keepHilite is true:

 if (keepHilite) {

The second statement modifies the condition further. It checks the value of the container menu's currentItem property. Recall that the currentItem property of all menus was set to null in menuSetup(). It will still be null if the item-in-question is the first item we have moused over. If currentItem has no value (i.e. it is null), the second part of the && logical operator is not executed. If currentItem has a value then we check to see if it is equal to the item we are working with. If it is not the same, then the we move on to execute the statements:

    if (this.container.currentItem && this.container.currentItem != this) {

In plain English, the above line reads: "If the item the mouse is over is not the first item moused over nor is it the last item moused over in this menu, then execute the contained statements." We thus avoid unnecessary flicker that would occur if we de-highlighted an item that was then highlighted.

Thus, if the item passes the conditional statements, it means that a different item in the menu is the "current item," is presently highlighted, and needs de-highlighting. For Navigator, we change the curent item's background color, as before:

      if (NS4) {
        this.container.currentItem.bgColor = this.container.menuBGColor;

If NSfontover is true, we must also change the font color. Since the string we will write to the element contains a link, we make sure that it renders in the correct color, by setting the linkColor property of the item element's document. We then write the string stored in the item's htmStr property. Recall that this is the standard non-highlighted string.

        if (NSfontOver) {
          with (this.container.currentItem.document) {
            linkColor = this.container.menuFontColor;
            write(this.container.currentItem.htmStr);
            close();
          }
        }
      }

For Explorer users, we simple assign the relevant tree-specific values to the backgroundColor and color properties of the item's style:

      else {
        with (this.container.currentItem.style) {
          backgroundColor = this.container.menuBGColor;
          color = this.container.menuFontColor;
        }
      }
    }
  }

Now, we have de-highlighted any already-highlighted item, if keepHilite is true. At this stage there are no highlighted items. We proceed to highlight the item the mouse is over. The statements are as in other versions, only with tree-specific values. For Navigator users, the item's htmStrOver string is written to the item, if NSfontOver is true.

  if (IE4) {
    theEvent = menuLoc.event;
    if (theEvent.srcElement.tagName == "IMG") return;
    this.style.backgroundColor = this.container.menuBGOver;
    this.style.color = this.container.menuFontOver;
  }
  else {
    this.bgColor = this.container.menuBGOver;
    if (NSfontOver) {
      this.document.write(this.htmStrOver);
      this.document.close();
    }
  }

The "old" item has been possibly de-highlighted and the new one highlighted. This new item is assigned to the menu's currentItem property, since it is now the "current item."

  this.container.currentItem = this;

As before, any visible child menu, opened by the "old" item, is hidden:

  if (this.container.hasChildVisible) {
    this.container.hideChildren(this);
  }

The final group of statements opens the child menu of the item, if one exists, with two modifications from version 2:

  1. Every menu in a tree has its own tree-specific childOverlap property, instead of the version 2 global childOverlap.
  2. Menu trees can cascade from right to left even in non-frame documents. Recall that the value of isRight reflects this choice of behavior. We therefore, check the value of isRight and assign either a positive or negative pixel value to a new variable, horOffset. This pixel value is attained by subtracting either the overlap from the menu width (regular cascade) or the width from the overlap (right-to-left).

    We then add horOffset to the menu's left page position to get the child menu's left position.

  if (this.hasMore) {
    horOffset = (isRight) ?
[cc]   (this.container.childOverlap - this.container.menuWidth) :
[cc]   (this.container.menuWidth - this.container.childOverlap);
    if (NS4) {
      this.childX = this.container.left + horOffset;
      this.childY = this.pageY + childOffset;
    }
    else {
      this.childX = this.container.style.pixelLeft + horOffset;
      this.childY = this.offsetTop +
[cc]   this.container.style.pixelTop + childOffset;
    }

itemOver ends with the same five statements as in version 2. The child menu is positioned, properties are set and it is made visible:

    this.child.moveTo(this.childX,this.childY);
    this.child.keepInWindow();
    this.container.hasChildVisible = true;
    this.container.visibleChild = this.child;
    this.child.showIt(true);
  }
}

Item mouseout

The itemOut() function is exteremely straightforward. It only changes an item's highlight (de-highlights) if keepHilite is false. It also checks NSfontOver and writes to the item to change the font color.

function itemOut() {
  if (IE4) {
    theEvent = menuLoc.event;
      if (theEvent.srcElement.contains(theEvent.toElement)
     || (theEvent.fromElement.tagName=="IMG" &&
[cc]   theEvent.toElement.contains(theEvent.fromElement)))
        return;
    if (!keepHilite) {
      this.style.backgroundColor = this.container.menuBGColor;
      this.style.color = this.container.menuFontColor;
    }
  }
  else {
    if (!keepHilite) {
      this.bgColor = this.container.menuBGColor;
      if (NSfontOver) {
        with (this.document) {
          linkColor = this.container.menuFontColor;
          write(this.htmStr);
          close();
        }
      }
  
    }
    if (!isOverMenu && !clickKill) {
      allTimer = setTimeout("currentMenu.hideTree()",10); 
    }
  }
}

As in version 2, when the item is moused out in Navigator and we are no longer over any menu (!isOverMenu), the allTimer timer is set to hide the menu tree. The difference in this version is that allTimer is not set if clickKill is true. In that case, we wait for the user to click outside the menus to hide them.

On the next page, we'll look at the functions related to menu positioning and display.


Produced by Peter Belesis and

All Rights Reserved. Legal Notices.
Created: Sept. 03, 1998
Revised: Sept. 03, 1998

URL: http://www.webreference.com/dhtml/column21/hier3Itover.html