DHTML Lab: Hierarchical Menus Version 3 | 18 | WebReference

DHTML Lab: Hierarchical Menus Version 3 | 18


Logo

Hierarchical Menus: Version 3
menu positioning

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 = 140;
childOffset = 5;
perCentOver = 30;
secondsVisible = 1;
fntCol = "black";
fntSiz = "10";
fntBold = true;
fntItal = false;
fntFam = "serif";
backCol = "#3399FF";
overCol = "#55BBFF";
overFnt = "black";
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:

moveTo():
  column 15

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

showIt():
  column 14
  column 15

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.

moveTo()

As before, the moveTo() function, for positioning a menu element, is IE-specific, and imitates the NS built-in moveTo() method.

function moveTo(xPos,yPos) {
    this.style.pixelLeft = xPos;
    this.style.pixelTop = yPos;
}

keepInWindow()

The keepInWindow() function, which ensures the placement of a menu within the browser window, has similar logic to its previous incarnation, but accounts for the new version 3 features:

function keepInWindow() {
  scrBars = 20;
  botScrBar = (isFrames&&navFrLoc=="bottom") ? (borWid*2) : scrBars;
  rtScrBar = (isFrames&&navFrLoc=="right") ? (borWid*2) : scrBars;
  if (NS4) {
    winRight = (menuLoc.pageXOffset + menuLoc.innerWidth) - rtScrBar;
    rightPos = this.left + this.menuWidth;
   
    if (rightPos > winRight) {
      if (this.hasParent) {
        parentLeft = this.parentMenu.left;
        newLeft = ((parentLeft-this.menuWidth) + this.childOverlap);
        this.left = newLeft;
      }
      else {
        dif = rightPos - winRight;
        this.left -= dif;
      }
    }
    winBot = (menuLoc.pageYOffset + menuLoc.innerHeight) - botScrBar ;
    botPos = this.top + this.fullHeight;
    if (botPos > winBot) {
      dif = botPos - winBot;
      this.top -= dif;
    }
    
    winLeft = menuLoc.pageXOffset;
    leftPos = this.left;
    if (leftPos 
  }
  else {
    winRight = (menuLoc.document.body.scrollLeft +
[cc]   menuLoc.document.body.clientWidth) - rtScrBar;
    rightPos = this.style.pixelLeft + this.menuWidth;
  
    if (rightPos > winRight) {
      if (this.hasParent) {
        parentLeft = this.parentMenu.style.pixelLeft;
        newLeft = ((parentLeft - this.menuWidth) + this.childOverlap);
        this.style.pixelLeft = newLeft;
      }
      else {
        dif = rightPos - winRight;
        this.style.pixelLeft -= dif;
      }
    }
    winBot = (menuLoc.document.body.scrollTop +
[cc]   menuLoc.document.body.clientHeight) - botScrBar;
    botPos = this.style.pixelTop + this.fullHeight;
    if (botPos > winBot) {
      dif = botPos - winBot;
      this.style.pixelTop -= dif;
    }
    
    winLeft = menuLoc.document.body.scrollLeft;
    leftPos = this.style.pixelLeft;
    if (leftPos 
  }
}

The first modification concerns the scrBars variable, used to account for window/frame visible scrollbars. Experience has shown that frameset authors, who place the navigation frame on the right or bottom, omit scrollbars to enable the menus to appear adjacent to the navigation links. The second and third statements, below, account for this by creating two new variables, botScrBars and rtScrBars.

If the navigation frame is on the bottom, the lack of bottom scrollbars is assumed, and the only "extra space" calculated is the menu border widths. This places the menu right up against the bottom frame. If the navigation frame is not on the bottom, then botScrBar takes the value of scrBars, creating more space below the menu. This is more suitable for menus that have appeared from a left navigation frame, for example, and have reached the bottom while cascading.

The same logic is applied to rtScrBars:

  scrBars = 20;
  botScrBar = (isFrames && navFrLoc=="bottom") ? (borWid*2) : scrBars;
  rtScrBar = (isFrames && navFrLoc=="right") ? (borWid*2) : scrBars;

The two groups of browser-specific statements that follow are different only in their use of browser-specific properties, so we will look at only the Navigator version in detail. The code below checks for a menu positioned to the right off the visible window/frame, and "pulls" it back in, cascading child menus to the left of any parent. It is exactly like the version 2 statements, only it uses rtScrBar instead of scrBars and tree-specific parameters instead of global (menuWidth and childOverlap).

winRight = (menuLoc.pageXOffset + menuLoc.innerWidth) - rtScrBar;
rightPos = this.left + this.menuWidth;
   
if (rightPos > winRight) {
  if (this.hasParent) {
    parentLeft = this.parentMenu.left;
    newLeft = ((parentLeft-this.menuWidth) + this.childOverlap);
    this.left = newLeft;
  }
  else {
    dif = rightPos - winRight;
    this.left -= dif;
  }
}

Similarly, the code that retains the menu from "going off the bottom" substitutes botScrBars for scrBars. Otherwise, it is exactly as before. Notice that this is where we use the fullHeight property of the menu, set in menuSetup().

winBot = (menuLoc.pageYOffset + menuLoc.innerHeight) - botScrBar ;
botPos = this.top + this.fullHeight;
if (botPos > winBot) {
  dif = botPos - winBot;
  this.top -= dif;
}

The next group of statements is new to version 3, and keeps the menus from disappearing off the left of the screen. This may occur when menus are cascaded from right-to-left. Admittedly, this was an omission in previous versions. There are no left scrollbars, so we don't account for them. If the menu is a child, it is cascaded to the right of its parent. If the menu is a top-level menu, it is placed five pixels in from the left:

winLeft = menuLoc.pageXOffset;
leftPos = this.left;
if (leftPos 

showIt()

In previous versions, showIt() simply toggled element visibility. In version 3, showIt() has an additional task. Recall that if keepHilite is true, the highlighted item is de-highlighted when a different item in the same menu is moused over. If we mouse out of the menu completely and it is hidden (or we click and it is hidden), the "current item" is still highlighted. We must, therefore, restore any highlighted item to the default state, so the next time the menu appears no item is highlighted. To do this we check the value of keepHilite and for the existence of a current item. When showIt() exits, it sets the menu's currentItem property to null, as there is no highlighted item:

function showIt(on) {
    if (NS4) {
        this.visibility = (on) ? "show" : "hide";
        if (keepHilite && this.currentItem) {
            this.currentItem.bgColor = this.menuBGColor;
            if (NSfontOver) {
                with (this.currentItem.document) {
                    write(this.currentItem.htmStr);
                    close();
                }
            }
        }
    }
    else {
        this.style.visibility = (on) ? "visible" : "hidden";
        if (keepHilite && this.currentItem) {
            with (this.currentItem.style) {
                backgroundColor = this.menuBGColor;
                color = this.menuFontColor;
            }
        }
    }
    this.currentItem = null;
}

Only a few more functions to go, script fans. Next, a look at the "hide" functions.


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/hier3Show.html