DHTML Lab: Hierarchical Menus, I; Item Setup | WebReference

DHTML Lab: Hierarchical Menus, I; Item Setup


Logo

DHTML Hierarchical Menus, Part I
item setup

Webreference

Contents

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

Unlike its menu counterpart, the function that defines the item setup, itemSetup() does not assign many methods to the item element object. Instead, it defines and sets properties, and actually builds the item.

function itemSetup(arrayPointer,whichArray) {
   this.bgColor = backCol;
   this.clip.right = menuWidth;
   this.visibility = "inherit";
   this.itemOver = itemOver;
   this.itemOut = itemOut;
   this.onmouseover = this.itemOver;
   this.onmouseout = this.itemOut;
   this.dispText = whichArray[arrayPointer];
   this.linkText = whichArray[arrayPointer + 1];
   this.hasMore = whichArray[arrayPointer + 2];
   if (this.linkText.length > 0) {
      this.linkIt = linkIt;
      this.onfocus = this.linkIt;
   }
     
   this.container = this.parentLayer;
      
   if (this.hasMore) {
      htmStr = imgStr + this.dispText;
   }
   else {
      htmStr = this.dispText;
   }
   layStr = "<SPAN CLASS=items>" + htmStr+ "</SPAN>";
   this.document.write(layStr);
   this.document.close();
   if (arrayPointer == 0) {
      this.top = 0;
   }
   else {
      this.top = (keep with next)
this.prevItem.top + this.prevItem.document.height - borWid;
   }
   this.left = 0;
}

Step by Step

First, we set three built-in properties, common to all positioned elements:

   this.bgColor = backCol;
   this.clip.right = menuWidth;
   this.visibility = "inherit";

The background color (bgColor) is set using the parameter variable, backCol, that we defined in-page. The visible width (clip.right) of the item is set using menuWidth. Since we want our items to be shown and hidden depending on the visibility of the containing menu, the visibility property is set to inherit.

Note: In Navigator, positioned elements defined in the HTML page flow have a default visibility value of inherit. Since the top level object (the window) is visible, this results in the elements being visible. Elements created after page load have a default visibility of hide and their visibility property must be set programatically.

Like menus, items have onmouseover and onmouseout event handlers:

   this.itemOver = itemOver;
   this.itemOut = itemOut;
   this.onmouseover = this.itemOver;
   this.onmouseout = this.itemOut;

The new item properties, itemOver and itemOut direct the handlers to execute the itemOver() and itemOut() functions whenever the mouse moves over or out of the item.

Three new properties, dispText, linkText and hasMore, are created to store the three array elements associated with the item:

   this.dispText = whichArray[arrayPointer];
   this.linkText = whichArray[arrayPointer + 1];
   this.hasMore = whichArray[arrayPointer + 2];

Recall that our array pointer is incremented by three (3), and we passed the present pointer to itemSetup as its first argument (arrayPointer). The pointer itself is pointing to the part of the array that contains the item display text. If we increment the pointer by one, it will point to the item URL. Incrementing the pointer by two, positions it at the 0 or 1 that denotes whether the item opens a child menu. For clarity, we assign these three values to item properties.

Next, we check the length of linkText to determine if it is a blank string:

   if (this.linkText.length > 0) {
      this.linkIt = linkIt;
      this.onfocus = this.linkIt;
   }

If linkText is a blank string, the item was not intended to be a link. If it is not a blank string (linkText.length > 0) then the linkIt() function is assigned to the item's linkIt method. Finally we instruct the linkIt method to be executed whenever the item gets user focus, by assigning it to the item's onfocus event handler.

onFocus??? Why not onClick?
Since we are dealing with a link, it might seem obvious that we should connect it with the click event. In Navigator, it's not that simple. Positioned elements do not have an onclick event handler. Their document property/object does, however. But the click event must be captured before it is used. Thus, we could say:

   this.document.captureEvents(Event.CLICK);
   this.document.onclick = this.linkIt;

Those of you who have tried this will have noticed that one has to click outside the text contained in the element to reach the element's document. Too messy, and workarounds are necessary.

Another obvious option would be to enclose the display text in link tags (<A HREF=> / </A>). Then, unfortunately, it would be necessary to click on the text itself, to navigate to the linked URL.

We want to click anywhere in the item rectangle. The onFocus event handler of the element itself, fires whenever the user gives focus to the element. That is, whenever the user clicks anywhere in the element. In effect, we are using click, but in a not-so-immediately-apparent way. Added bonus: the focus event does not need to be captured.

Next, another new property, container, is defined:

   this.container = this.parentLayer;

Since we have established our own parent/child naming scheme, it might confuse to use the built-in parentLayer property that identifies the element within which the present element is nested. Also, creating a new property will assist us in the final cross-browser code we will create.

We are now ready to build the displayed version of the item. We create a string, htmStr, that stores the text-to-be-displayed (dispText). If the item opens a child menu (hasMore), then the triangle image string (imgStr) defined earlier is prefixed to the display text.

   if (this.hasMore) {
      htmStr = imgStr + this.dispText;
   }
   else {
      htmStr = this.dispText;
   }
   layStr = "<SPAN CLASS=items>" + htmStr+ "</SPAN>";

htmStr is then enclosed in a SPAN element with a CLASS attribute value of items. Anything within the SPAN will have the property values set earlier in our stylesheet. Do not confuse this use of SPAN with SPAN as a positioned element. Here, we use SPAN as a neutral container. This new string is stored in layStr, which is then written to the item using document.write():

   this.document.write(layStr);
   this.document.close();

We have now created and styled the item. All that remains is to position it within its container menu:

   if (arrayPointer == 0) {
      this.top = 0;
   }
   else {
      this.top = (keep with next)
this.prevItem.top + this.prevItem.document.height - borWid;
   }
   this.left = 0;

If this is the first item being created for this menu, the arrayPointer will be positioned at the first element of the array, that is, at 0. If this is the case, we position the item at the menu top with a 0 value for the item's top property.

If the item is not the first item, it is positioned below (i.e., following) the previous item. Recall that in makeTop(), before calling itemSetup, we defined a prevItem property for every item, that stored the item previous to it in the menu. We find where this previous item ends vertically, by adding its top property to its document.height. We can then position our item directly below the previous one. If we use borders, we might want to overlap each item, to avoid double thickness borders, so we subtract borWid (i.e., we move the item up by the width of the border).

Finally, we set the item's left property to 0, placing it on the left edge of the menu.

We have setup our menu, mostly by assigning methods to it, and we have set up our items, by assigning properties and actually building and positioning each item within the menu. Before we can actually use the menus and discuss their behaviour, we still have to build the child menus.


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