DHTML Lab: Hierarchical Menus Version 3 | 10 | WebReference

DHTML Lab: Hierarchical Menus Version 3 | 10


Hierarchical Menus: Version 3
Explorer menu creation


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;
childOverlap = 20;
childOffset = 0;
perCentOver = null;
secondsVisible = .5;
fntCol = "blue";
fntSiz = "10";
fntBold = false;
fntItal = false;
fntFam = "sans-serif";
backCol = "#DDDDDD";
overCol = "white";
overFnt = "black";
borWid = 2;
borCol = "brown";
borSty = "solid";
itemPad = 6;
imgSrc = "tri.gif";
imgSiz = 10;
separator = 1;
separatorCol = "red";
isFrames = false;
keepHilite = true; 
NSfontOver = false;
clickStart = true;
clickKill = true;
showVisited = "";

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.

In previous versions, we have been creating the menus for IE4 with the most "elegant" and code-efficient method. That is, all menu items were positioned elements inserted into the menu positioned elements. This corresponded with the Navigator way of thinking, so the menu-creation code could be more-or-less cross-browser. This led, unfortunately, to two problems:

  1. The IE4 menu creation was much slower than NS4, as each item had to be inserted into the HTML and the page re-thought. For example, one menu tree, with 4 menus, each containing 10 items amounted to 44 insertions of positioned elements in the page (4 + (4x10)). In version 3, only the menus are inserted. In the above example, only four positioned elements, the menus themselves, would be inserted.
  2. The vertical positioning of the items within the menu could be off, depending on the speed of the user's system. Properties were not updated fast enough to keep up with the script. This led to two-line menu items being "chopped" after the first line.

These problems are solved if we use regular HTML for the items, instead of positioned elements. As we know, in IE4, all elements on a page can respond to events, so we can retain the mouseover/mouseout/click response of each item. This, of course, is not possible in NS4.

A menu with three items had this HTML in previous versions:

<DIV ID="elMenu1" STYLE="position:absolute">
<DIV ID="item1_1" STYLE="position:absolute"></DIV>
<DIV ID="item1_2" STYLE="position:absolute"></DIV>
<DIV ID="item1_3" STYLE="position:absolute"></DIV>

In version 3, the same menu, given a width of 120, has this HTML:

<DIV ID="elMenu1" STYLE="position:absolute">
<SPAN ID="item1_1" STYLE="width:120"></SPAN><BR>
<SPAN ID="item1_2" STYLE="width:120"></SPAN><BR>
<SPAN ID="item1_3" STYLE="width:120"></SPAN><BR>
This item is on more than one line

If you are using IE4, there is a working example of this type of menu on the left. The <SPAN> element takes a width value, immediately, to ensure proper text-wrapping. All other styling will be performed by the script later. The SPANs are created as one long string, then placed in the menu positioned element with innerHTML.

All this is handled by the makeMenuIE() function, called by makeTop():

function makeMenuIE(isChild,menuCount,parMenu) {
  menu = makeElement("elMenu" + menuCount);
  menu.array = eval("arMenu" + menuCount);
  menu.setMenuTree = setMenuTree;
  menu.itemStr = "";
  while (menu.itemCount < menu.maxItems) {
    status = "Creating Hierarchical Menus: "
[cc]   + menuCount + " / " + menu.itemCount;
    itemName = "item" + menuCount + "_" + menu.itemCount;
    arrayPointer = (isChild) ?
[cc]   (menu.itemCount-1)*3 :((menu.itemCount-1)*3)+9;
    dispText = menu.array[arrayPointer];
    hasMore = menu.array[arrayPointer + 2];
    htmStr = (hasMore) ? imgStr + dispText : dispText;
    menu.itemStr += "<SPAN ID=" + itemName +
[cc]   " STYLE=\"width:" + menu.menuWidth + "\">" + htmStr + "</SPAN><BR>";
    if (menu.hasMore) {
      makeMenuIE(true,menuCount + "_" + menu.itemCount,menu);
      menu = menu.parentMenu;
  menu.innerHTML = menu.itemStr;
  itemColl = menu.children.tags("SPAN");
  for (i=0; i<itemColl.length; i++) {
    it = itemColl(i);
    it.setup = itemSetup;
  menu.lastItem = itemColl(itemColl.length-1);

The first two statements are the same as the version 2 makeMenu(). The menu element is created and assigned to the menu variable/object, and the relevant array is assigned to the menu's array property.

As with the NS4 menu creation, the setMenuTree() method is assigned and called to create the tree-specific properties.

Then, we go on to create the item element string, by initializing the menu.itemStr property to an empty string. The usual while statement loops through all items defined in the array to create the string. For each item, it:

  1. determines the position in the array that corresponds to the current item definition (arrayPointer)
  2. assigns the item's display text to the temporary dispText property variable
  3. determines if the item has a related child menu and sets the temporary hasMore variable
  4. creates the htmStr temporary variable to store the item's display HTML. If the item has a child menu (hasMore), the image string (imgStr) is appended to the text string (dispText). Otherwise, htmStr is simply assigned dispText
  5. expands the item element string (menu.itemStr) to include:
    1. the opening SPAN tag, the item ID and width
    2. the item display text
    3. the closing SPAN tag
    4. a line break to force the next item to a new line.
  6. calls makeMenuIE() if a child menu needs to be created

When we exit the while loop, menu.itemStr contains all the HTML necessary to create the menu items, so it is placed within the menu with innerHTML. All the items, therefore are created at once.

The items, however, have yet to be "set up" and properties assigned. In previous versions, we set each item up immediately upon creation. In version 3, we must identify the item elements after creation and assign and call their setup() method. We, therefore, create a collection of all item elements by creating a collection of all SPAN elements contained within the menu (menu.children.tags("SPAN")). We then loop through the items, calling itemSetup() for each. The last item in each menu is identified and assigned to the menu's lastItem property. Recall that we need to know the last item in each menu so we can remove its bottom border, the unnecessary "separator" line, later.

With the items set, we finally call the menu's setup() method to "set up" the menu itself.

This method of Explorer element-creation results in a menu-creation delay that is about half the version 2 delay. A significant improvement.

On the next page, we'll look at the itemSetup() function.

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