DHTML Lab: Hierarchical Menus Ver. 2 (Cross-Browser/Frames); Initializing Menu Creation | WebReference

DHTML Lab: Hierarchical Menus Ver. 2 (Cross-Browser/Frames); Initializing Menu Creation


Logo

Hierarchical Menus Ver. 2 (Cross-Browser/Frames)
initiating menu creation

WebReference

Parameters used for the menus on this page:

menuWidth = 120;
childOverlap = 1;
childOffset = 5;
perCentOver = null;
secondsVisible = .5;
fntCol = "black";
fntSiz = 10;
fntBold = false;
fntItal = false;
fntFam = "sans-serif";
backCol = "white";
overCol = "#FFCC00";
overFnt = "black";
borWid = 2;
borCol = "black";
borSty = "solid";
itemPad = 3;
imgSrc = "tri.gif";
imgSiz = 10;
separator = 0;
separatorCol = "red";
isFrames = false;

Menu Animated GIF
Animated GIF demonstrating heirarchical menus for non-DHTML browsers.

In longer 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 makeTop() function has been completely changed from Version 1. Its menu-making statements have been transferred to a new function, makeMenu(). In Version 2, makeTop() sets global variables, calls makeMenu() for every top-level menu, and finally formats the menus for IE4, when all menus have been created.

function makeTop(){
  beingCreated = true;
    
  while(eval("window.arMenu" + topCount)) {
    makeMenu(false,topCount);
    topCount++
  }
  if (IE4) {
    divColl = menuLoc.document.all.tags("DIV");
    for (i=0; i<divColl.length; i++) {
     if (divColl[i].id.indexOf("elMenu")!=-1) {
      divColl[i].fullHeight =
[cc]  divColl[i].lastItem.style.pixelTop +
[cc]  divColl[i].lastItem.scrollHeight + (borWid*2);
       divColl[i].style.height = divColl[i].fullHeight;
     }
   }
 }    
    
  status=(topCount-1)+" Hierarchical Menu Trees Created"
  areCreated = true;
  beingCreated = false;
}

First, makeTop() sets the beingCreated to true, since the menus are now in the process of being created.

Next, we have our old while loop, only in this incarnation, it has only two statements. The first one references our arrays and calls makeMenu() for every top-level menu to be created; the second increments topCount. We break out of the loop when our menus are done.

makeMenu() is passed two arguments, the first is a Boolean specifying the existence of a parent menu. Since makeTop() calls makeMenu() to create top-level menus, this argument is false. The second argument is the top-level menu count, topCount.

Following this, we have Explorer-specific code for menu sizing, discussed immediately below.

Finally, makeTop() displays the number of menus created in the status bar as a courtesy, and since the menus are complete, areCreated becomes true and beingCreated, false.

Taking Explorer to New Heights

Note: This section deals with the Explorer code in makeTop(). Since it is executed after all the other functions we will be discussing, it might be advisable to read this part once you are familiar with the script as a whole.

In Version 1, we specified borders for all menu items. Version 2 specifies a border for the menu element. If an element, positioned or not, contains regular, non-positioned elements, it will expand to enclose them within its border (see our IE4 Puzzle for an example). If an element contains positioned elements, it will not expand. Our items are positioned elements nested within the menu elements. We must therefore size the menu through script, by adding up the heights of the items contained, in the same way that we clip the menu height in the Navigator-specific code.

Problem 1: Regular readers of this column may remember that in the cross-browser version of the Puzzle, we had difficulties reading an image's size properties immediately upon image definition. We have yet to figure out when Explorer updates element properties, or how to force it to update them! In our present situation, we originally had the script expanding the menu size after the creation of each item. This was fine, until an item had wrapped text on two lines. Explorer considered it to be a single line!! Given time, it recognized the true height! By given time, we mean reading the height properties after a time interval. A simple debug alert window after element creation, but before property reference, sufficed. Sizing the menus at the end of our script, in makeTop(), solved this problem.

Problem 2: Explorer offers two read-only element properties that store the height of an element: scrollHeight and offsetHeight. The first, scrollHeight, is a property of some control elements (BODY, BUTTON, DIV, FIELDSET, FRAME, IMG, MARQUEE, SPAN and TEXTAREA), while offsetHeight is a property of all elements. Our items (DIVs) have both properties.

The Microsoft SDK describes offsetHeight in this way:

"Returns the height of the element, in pixels, relative to the parent coordinate system."
offsetHeight.htm

And scrollHeight, in this way:

"Returns the element's scrolling height, in pixels. This is the distance between the top and bottom edges of the element's visible content. The physical height of the content, including the nonvisible content, can be obtained with the offsetHeight property."
scrollHeight.htm

Both of the documents above have links to the article, Measuring Element Dimension and Location, which includes this image:

Microsoft Copyright Notices for Materials Used

It's obvious that the SDK states the opposite of what the image defines. It gets worse.

If no "scrolling" is specified, that is, if the complete element is visible, then following either of the contradictory property definitions above, scrollHeight and offsetHeight should be the same!

If you are using Explorer 4, you can view a live example of a fully-visible element returning different values for the two properties. For non-Explorer 4 users, a screen shot of the page follows:

Believe me, I tried to figure this out. The logic escapes me, although I realize there may be a forest-trees explanation. If anyone can forward a definition of these two properties that makes sense, I'd be grateful.

For now, we've used offsetHeight for positioning the items under each other and scrollHeight here, in the delayed menu sizing:


divColl = menuLoc.document.all.tags("DIV");
for (i=0; i<divColl.length; i++) {
  if (divColl[i].id.indexOf("elMenu")!=-1) {
    divColl[i].fullHeight =
[cc]  divColl[i].lastItem.style.pixelTop +
[cc]  divColl[i].lastItem.scrollHeight + (borWid*2);
    divColl[i].style.height = divColl[i].fullHeight;
  }
}

First, we create a collection of all page elements that have the DIV tag, and assign it to divColl. Next we loop through divColl, isolating those DIVs that have "elMenu" in their ID. All menus have a lastItem property that has been tracking the last-item-created for that menu. By adding the top position of this last item to its scrollHeight, we get the full height of all the menu items stacked top-to-bottom. We add to this the menu border width (times 2, for top and bottom borders) and we have the necessary full height of the menu. We size the menu to this height by assigning the value to the menu's style.height property.

And who says we don't learn anything here at DHTML lab?

Back to the script now, for a look at makeMenu(), that actually creates the menus.


Produced by Peter Belesis and

All Rights Reserved. Legal Notices.
Created: May. 22, 1998
Revised: May. 22, 1998

URL: http://www.webreference.com/dhtml/column20/hier2Begin.html