[an error occurred while processing this directive]
> developer > web app development
Building Hierarchical Navigation Menus Using JavaScript
by Murali Kashaboina, Senior Software Engineer, Novell
Date Created: 2001-03-26 13:28:00.000
  Introduction
  Design Model
  MenuTree
  Menu
  MenuItem
  Example
  Suggested Improvements
  Conclusion
  References

introduction
Site navigation design is one of the most important steps to consider during the development process. Navigation elements, such as bars and menus, enable faster browsing and are considered good design practice because they offer the following advantages:

  • Support a Web site's overall look and feel
  • Provide ready answers to users' questions
  • Reflect users' tasks and facilitate movement to and from selected pages

To create a user-friendly site, page navigation should be designed intuitively and intelligently. One way to accomplish this is to incorporate a hierarchical menu that looks similar in outlook and functionality to the Windows Explorer folder tree menu.

A hierarchical menu offers the advantage of providing flexibility to group navigation elements based on the importance and context of those elements. And because a hierarchical menu can be collapsed or expanded as needed by the user, it occupies a minimum of page space while still adding to the look and feel of the page.

Now the question arises: How can a hierarchical menu be designed so that it is intelligent enough to expand or collapse on users' clicks? But, from a developer's point of view, there is an even more important question: How can there be a programmatic control on the menu behavior when the content on the browser is written in static HTML?

JavaScript offers the first glimmer of hope. JavaScript is a powerful, web-oriented language that developers can use to solve development dilemmas such as the ones mentioned above.

This article describes one way to develop folder-like menus using JavaScript and HTML DIV elements. DIV elements in HTML offer many properties that can be used to control DIV element behavior. For example, the 'VISIBLE' property allows a developer to set a DIV element's visibility to either 'hidden' or 'visible'. Other DIV element properties, such as LEFT and TOP (pixel left and pixel top in Internet Explorer), enable DIV elements to move dynamically on the Web page. All of these properties are accessible using JavaScript, and with JavaScript developers can control these properties programmatically.
design model
For the purpose of example, the following tree-like menu was designed using the properties and JavaScript access as indicated below. An object-oriented approach was taken in designing this menu to avoid redundant code, enable the reuse of existing functionalities, restrict a set of functionalities to meaningful objects, and most important, to impose clarity and simplicity on the code so that it could be easily understood. This approach also yielded further advantage in that it simplified the actual code construct of the menu.

The design model consists of three main JavaScript objects namely 'MenuTree', 'Menu', and 'MenuItem'. 'MenuTree' is the objectified representation of the navigation system that contains a tree of menu objects. 'Menu' is the primary node in the hierarchy, and 'MenuItem' is a sub-node under a menu node. This means that a menu object can contain one or more menu items.

Following is an object diagram for these objects:



The actual JavaScript code for these objects would be as follows:

/**This variable verifies the browser type*/

var isIEXPLORER = (document.all) ? 1:0;



/**This variable is used to set the menu mode to collapsed*/

var COLLAPSE_MODE = 0;



/**This variable is used to set the menu mode to half expanded*/

var HALF_EXPAND_MODE = 1;



/**This variable is used to set the menu mode to full expanded*/

var FULL_EXPAND_MODE = 2;



/**This variable is used to set the vertical gap between the menu & submenu and between submenus*/

var VSUBMENUGAP = 20;



/**This variable is used to set the horizontal gap between the menu & its submenus*/

var HSUBMENUGAP = 15;



/**This variable is used to set the vertical gap between a menu and its immediate above menu or submenu*/

var VMENUGAP = 30;



/**This variable is used to set the horizontal gap between a menu and the left container( like browser) border */

var MENU_LEFT = 20;



/**This variable is used to set the font size*/

var FONT_SIZE = 1;



/**This variable is used to set the font color*/

var FONT_COLOR = "white";



/**This variable is used to set the font face*/

var FONT_FACE = "VERDANA";



/**This variable is of MenuTree type and contains all the menus in the app*/

var menuTree = null;



/**This variable is a temporary variable used to store display mode for a menu for which a request is made to update even before the menu is completely built. Do not disturb its value at anytime in the page*/

var TEMPMODE = 0;



/**This variable is a temporary variable used to store an index of a menu for which a request is made to update even before the menu is completely built. Do not disturb its value at anytime in the page*/

var TEMPINDEX = 0;



/**This variable is used to indicate if a request has been received to update a menu even before the menu is completely built. Do not disturb its value at anytime in the page.*/

var ISUPDATECALLED = false;



/**This variable is used to indicate if the menu is being built and is used in updateMenu method to check if menu has already been built. Do not disturb its value at anytime in the page.*/

var ISMENUBUILDING = true;



/**This is the constructor for the MenuItem or Submenu object used in the menu system. As it is evident from the method signature, it takes five main parameters namely, unique name, displayString, link value, target frame and the parent menu index. Do not disturb its code because it is very sensitive and menu may misbehave drastically*/

function MenuItem( name, displayString, link, linkTarget, menuIndex )

{

this.name = name;

this.displayString = displayString;

this.menuIndex = menuIndex;

this.link = link;

this.linkTarget = linkTarget;

this.top = 0;

this.left = 0;

this.objID = 'SubMenu' + name + menuIndex;

this.writeMenuItemHtml = writeMenuItemHtml;

this.setVisible = setVisible;

}



/**This function is method in the MenuItem object that is used to render the MenuItem as a DIV Object in the html page.

Do not disturb its code*/

function writeMenuItemHtml()

{

document.write( "<font size=" + FONT_SIZE + " color = " +

FONT_COLOR + " FACE=\"" + FONT_FACE + "\">" );

hrefLink = "<div id=\""+this.objID+"\" class= \"SUBMENU\">";

hrefLink = hrefLink + " <a href = \"" + this.link + "\" target=" + this.linkTarget + ">" + this.displayString + "</a> </div>";

document.write( hrefLink );

document.write( "</font>" );

}



/**This funtion is a member function of MenuItem object that is used to either show or hide the MenuItem under a Menu.

Do not disturb its code */

function setVisible( blnValue )

{

obj = getDivObj( this.objID );

if( blnValue )

{

obj.visibility = "visible";

}

else

{

obj.visibility = "hidden";

}

}



/**This is the contructor for the Menu object used in the menu system. As it is evident from the method signature, it takes four main parameters namely, unique name, link value, target frame and the parent menu index. Do not disturb its code */

function Menu( name,displayString, link, linkTarget, index )

{

this.name = name;

this.link = link;

this.linkTarget = linkTarget;

this.index = index;

this.displayString = displayString;

this.mode = COLLAPSE_MODE;

this.top = 0;

this.left = 0;

this.objID = 'Menu' + name + index;

this.imageID = this.objID + 'Image';

this.mainImageSrc = "";

this.swapImageSrc = "";

this.halfModeItemsToDisplay = 1;

this.menuItemKeys = new Array();

this.addMenuItem = addMenuItem;

this.writeMenuHtml = writeMenuHtml;

this.setMode = setMode;

this.getItemCount = getItemCount;

this.getItemTop = getItemTop;

this.getItemLeft = getItemLeft;

this.setItemPositions = setItemPositions;

this.setMainImage = setMainImage;

this.setSwapImage = setSwapImage;

this.refreshImage = refreshImage;

}



/**This function is a member function of the Menu object that is used to refresh the image of the Menu depending upon its current mode of display. Do not disturb its code */

function refreshImage()

{

obj = getImageObj( this.imageID );

if( this.mode == COLLAPSE_MODE )

{

obj.src = this.mainImageSrc;

}

else

{

obj.src = this.swapImageSrc;

}

}



/**This is a member function of the Menu that is used to set the main image for the Menu to display*/

function setMainImage( imageSrc )

{

this.mainImageSrc = imageSrc;

}



/**This is a member function of the Menu that is used to set the swap image for the Menu to display*/

function setSwapImage( imageSrc )

{

this.swapImageSrc = imageSrc;

}



/**This is a member function of the Menu that is used to get the current Top property of a MenuItem given that menuItems index*/

function getItemTop( itemIndex )

{

menuItemKey = this.menuItemKeys[ itemIndex ];

menuItem = this[ menuItemKey ];

itemTop = menuItem.top;

return itemTop;

}



/**This is a member function of the Menu that is used to get the current Left property of a MenuItem given that menuItems index*/

function getItemLeft( itemIndex )

{

menuItemKey = this.menuItemKeys[ itemIndex ];

menuItem = this[ menuItemKey ];

itemLeft = menuItem.left;

return itemLeft;

}



/**This is a member function of the Menu object that is used to set the current Top and Left properties of all its MenuItems depending upon the Menu's current position*/

function setItemPositions()

{

newTop = this.top;

newLeft = this.left + HSUBMENUGAP;

for( i = 0; i < this.menuItemKeys.length; i++ )

{

newTop = newTop + VSUBMENUGAP;

menuItemKey = this.menuItemKeys[i];

menuItem = this[ menuItemKey ];

menuItem.top = newTop;

menuItem.left = newLeft;

obj = getDivObj( menuItem.objID );

if( isIEXPLORER )

{

obj.pixelTop = newTop;

obj.pixelLeft = newLeft;

}

else

{

obj.top = newTop;

obj.left = newLeft;

}

}

}



/**This is a member function of the Menu Object that is used to get the number of its MenuItems*/

function getItemCount()

{

count = this.menuItemKeys.length;

return count;

}



/**This is a member function of the Menu Object that is used to add a new MenuItem given the required parameters for the MenuItem construction. This method creates a new MenuItem and adds it to the Menu as property with the unique MenuItem name as the property name. Any JavaScript Object has an implicit array for storing explicit objects as properties. This technique is used to store MenuItems in a given Menu object. Do not change any code */

function addMenuItem( name, displayString, link, linkTarget )

{

newIndex = this.menuItemKeys.length;

this.menuItemKeys[ newIndex ] = name;

this[ name ] = new MenuItem( name, displayString,link, linkTarget, this.index );

}



/**This is a member method in the Menu object that is used to render the Menu as a DIV Object in the html page. This method not only displays the Menu as a DIV objects but it also invokes writeMenuItemHtml method on all the MenuItems of this Menu. Do not disturb its code */

function writeMenuHtml()

{

document.write( "<font size=" + FONT_SIZE + " color = " + FONT_COLOR + ">" );

hrefLink = "<div id=\""+this.objID+"\" class= \"MENU\">";

hrefLink = hrefLink + "<a href = \"" + this.link + "\" target=" + this.linkTarget + " onClick = \"updateMenu(" + this.index + ",HALF_EXPAND_MODE );\">";

hrefLink = hrefLink + "<img src = \"" + this.mainImageSrc + "\" NAME=\"" + this.imageID + "\" border=0 ></a> " + this.displayString + "</div>";

document.write( hrefLink );

document.write( "</font>" );



for( i = 0; i < this.menuItemKeys.length; i++ )

{

menuItemKey = this.menuItemKeys[i];

this[ menuItemKey ].writeMenuItemHtml();

}

}



/**This is a member function of the Menu Object that is used to set the display mode for the current Menu.The valid display modes are COLLAPSE_MODE,HALF_EXPAND_MODE,FULL_EXPAND_MODE as defined by the static page mode variables. Do not disturb any code*/

function setMode( mode )

{

this.mode = mode;

if( mode == FULL_EXPAND_MODE )

{

for( i = 0; i < this.menuItemKeys.length; i++ )

{

menuItemKey = this.menuItemKeys[i];

menuItem = this[ menuItemKey ];

menuItem.setVisible( true );

}

}

else if( mode == HALF_EXPAND_MODE )

{

for( i = 0; i < this.menuItemKeys.length; i++ )

{

menuItemKey = this.menuItemKeys[i];

menuItem = this[ menuItemKey ];

if( i < this.halfModeItemsToDisplay )

{

menuItem.setVisible( true );

}

else

{

menuItem.setVisible( false );

}

}

}

else

{

for( i = 0; i < this.menuItemKeys.length; i++ )

{

menuItemKey = this.menuItemKeys[i];

menuItem = this[ menuItemKey ];

menuItem.setVisible( false );

}

}

}



/**This is an empty contructor for a user defined MenuTree object in which all the menus are stored as properties of this object with menuKey as the name of the property*/

function MenuTree()

{

this.menuKeys = new Array();

this.addMenu = addMenu;

this.update = update;

this.write = write;

this.rePosition = rePosition;

}



/**This is a member method in the MenuTree object that is used to add Menus

function addMenu( menuName, menu, index )

{

this.menuKeys[ index] = menuName;

this[ menuName ] = menu;

}



/**This is a member method in the MenuTree object that is used to update MenuTree given a Menu's index and mode to display

function update( index, mode )

{

for( p = 0; p < this.menuKeys.length; p++ )

{

key = this.menuKeys[p];

menu = this[ key ];

if( p == index )

{

menu.setMode( mode );

}

else

{

menu.setMode( COLLAPSE_MODE );

}

menu.refreshImage();

}

this.rePosition();

}



/**This is a member method in the MenuTree object that is used to render the Menus as a DIV Objects in the html page*/

function write()

{

for( k = 0; k < this.menuKeys.length; k++ )

{

key = this.menuKeys[k];

menu = this[ key ];

menu.writeMenuHtml();

menu.setMode( COLLAPSE_MODE );

}

this.rePosition();

ISMENUBUILDING = false;

if( ISUPDATECALLED )

{

this.update( TEMPINDEX,TEMPMODE );

ISUPDATECALLED = false;

}

}







/**This is an important behavior critical method of MenuTree Object that is used to rePosition the entire Menu in the System depending upon their modes of display. Please do not disturb any code */

function rePosition()

{

newtop = 0;

newleft = 0;

p = 0;

key = this.menuKeys[p];

menu = this[ key ];

if( isIEXPLORER )

{

obj = eval('document.all.'+menu.objID);

newtop = obj.offsetTop;

newleft = obj.offsetLeft;

}

else

{

obj = eval('document.'+menu.objID);

newtop = obj.pageY;

newleft = obj.pageX;

}



for( p = 0; p < this.menuKeys.length; p++ )

{

key = this.menuKeys[p];

menu = this[ key ];

obj = getDivObj( menu.objID );

if( isIEXPLORER )

{

obj.pixelTop = newtop;

obj.pixelLeft = newleft;

}

else

{

obj.top = newtop;

obj.left = newleft;

}

menu.top = newtop;

menu.left = newleft;

menu.setItemPositions();

if( menu.mode == COLLAPSE_MODE )

{

newtop = menu.top + VMENUGAP;

}

else if( menu.mode == HALF_EXPAND_MODE )

{

i = menu.getItemCount();

if( i > 0 )

{

i = menu.halfModeItemsToDisplay - 1;

newtop = menu.getItemTop( i ) + VMENUGAP;

}

else

newtop = menu.top + VMENUGAP;

}

else

{

i = menu.getItemCount();

if( i > 0 )

{

i = i - 1;

newtop = menu.getItemTop(i) + VMENUGAP;

}

else

newtop = menu.top + VMENUGAP;

}

}

}



/**This is a general utility method that is used to return a DIV or Layer object given an object's unique id */

function getDivObj( obj )

{

if( isIEXPLORER )

{

obj = eval('document.all.'+obj+'.style');

}

else

{

obj = eval('document.'+obj);

}

return obj;

}



/**This is a general utility method that is used to return a Image object given an object's unique id */

function getImageObj( imageID )

{

obj = eval( 'document.' + imageID );

return obj;

}



/**This is a very important method that is invoked whenever there is a need to update the Menu display.Do not disturb any code */

function updateMenu( index, mode )

{

if( ISMENUBUILDING )

{

ISUPDATECALLED = true;

TEMPINDEX = index;

TEMPMODE = mode;

return;

}



menuTree.update();

return true;

}


As can be seen in the above code, three different modes of display were designed for 'Menu': collapsed mode, half-expanded mode, and full-expanded mode. The half-expanded mode was added to give additional flexibility and accommodate situations wherein designers would like to display only a limited number of menu items on certain page events. There are situations where 'Menu' will only display in full-expanded mode when certain pages are actually displayed. In all other click events, 'Menu' will display in half-expanded mode. To see a sample prototype depicting this kind of special functionality, use the following link:

http://members.tripod.com/~kbmurali/JavaScriptMenu/

The above code takes advantage of the implicit array contained by default in a JavaScript object. A JavaScript object has an implicit properties array in which each index element is associated with a string value, so any property can be accessed from the array as follows:

ObjectName[ "propertyID" ];

Now, consider the objects included in the design model.
MenuTree
'MenuTree' is the main object that represents a navigation system. The main methods in this object are 'addMenu', 'write', 'update', and 'rePosition'. The 'addMenu' method stores a menu object in the menu tree's properties array using the name specified. The example design model provided employs the implicit properties array technique discussed earlier. The 'addMenu' method implements as follows:


function addMenu( menuName, menu, index )

{

        this.menuKeys[ index] = menuName;

        this[ menuName ] = menu;

}



Looking at the above code, it is easy to see how useful an implicit properties array can be, particularly when there is no well defined object, such as Hashtable in Java, that can be used for storing objects. In order to access a given menu, reference it from the menu tree's implicit properties array using the above-mentioned technique.

If there is an explicit JavaScript array object to be stored in 'Menu Ids', use the 'write' method. This will invoke the 'writeMenuHtml' method from each menu in the array, doing it in the same order that the menus were added to the menu tree. The 'update' method is used to update the whole menu tree according to a menu's index and mode of display. The 'rePosition' method repositions the menus according to their mode of display. Following is the JavaScript object definition for 'MenuTree':


function MenuTree()

{

        this.menuKeys = new Array();

        this.addMenu = addMenu;

        this.update = update;

        this.write = write;

        this.rePosition = rePosition;

}

Menu
'Menu' is the primary node in the menu tree. A menu object stores different menu items in its implicit properties array. Note that in the sample given, an explicit array object was provided in the menu for storing all 'MenuItem Ids'. In order to access a given menu item, reference it from the menu's implicit properties array using the above-mentioned technique.

The main methods in the menu object are 'writeMenuHtml', 'addMenuItem', 'setMainImage', 'setSwapImage', 'setMode', 'refreshImage', 'getItemCount', and 'setItemPositions'. These methods enable the menu object to do the following respectively: add menu items; set the main image; set the swap image; set display mode; refresh the image depending upon the mode of display; get the number of menu items; and set the positions of menu items depending upon the menu's current position. The constructor for the menu item object requires a string parameter to display text and for its name, the hyper link, and the target frame name; it requires an int parameter for its index in the menu tree.

The JavaScript object declaration for the menu object is as follows:


function Menu( name,displayString, link, linkTarget, index )

{

                this.name = name;

        this.link = link;

                this.linkTarget = linkTarget;

                this.index = index;

        this.displayString = displayString;

        this.mode = COLLAPSE_MODE;

                this.top = 0;

                this.left = 0;

                this.objID = 'Menu' + name + index;

                this.imageID = this.objID + 'Image';

                this.mainImageSrc = "";

                this.swapImageSrc = "";

                this.halfModeItemsToDisplay = 1;

        this.menuItemKeys = new Array();

                this.addMenuItem = addMenuItem;

        this.writeMenuHtml = writeMenuHtml;

                this.setMode = setMode;

                this.getItemCount = getItemCount;

                this.getItemTop = getItemTop;

                this.getItemLeft = getItemLeft;

                this.setItemPositions = setItemPositions;

                this.setMainImage =  setMainImage;

                this.setSwapImage =  setSwapImage;

                this.refreshImage = refreshImage;

}



The 'addMenuItem' method takes four parameters that are required for constructing a menu item. It creates a menu item object and stores it in the implicit properties object using the menu item's ID. The 'addMenuItem' method implements as follows:


function addMenuItem( name, displayString, link, linkTarget )

{

newIndex = this.menuItemKeys.length;

this.menuItemKeys[ newIndex ] = name;

this[ name ] = new MenuItem( name, displayString,link, linkTarget, this.index );

}



The 'writeMenuHtml' method writes out the menu object as a DIV element of class DIV.MENU. DIV.MENU is a user defined style object that is specifically used for defining menu objects. This style definition is not actually part of JavaScript, but it should be defined as part of the header definition in the page that contains the tree menu. The 'writeMenuHtml' method implements as follows:


function writeMenuHtml()

{

document.write( "<font size=" + FONT_SIZE + " color = " + FONT_COLOR + ">" );

hrefLink =  "<div id=\""+this.objID+"\" class= \"MENU\">";

hrefLink = hrefLink + "<a href = \"" + this.link + "\" target=" + this.linkTarget + " onClick = \"updateMenu(" + this.index + ",HALF_EXPAND_MODE );\">";

hrefLink = hrefLink + "<img src = \"" + this.mainImageSrc + "\" NAME=\"" + this.imageID + "\" border=0 ></a>  " + this.displayString + "</div>";

document.write( hrefLink );

document.write( "</font>" );



               for( i = 0; i < this.menuItemKeys.length; i++ )

{

menuItemKey = this.menuItemKeys[i];

                this[ menuItemKey ].writeMenuItemHtml();

                }

}



Another important method is 'setItemPositions'. This method retrieves DIV objects for all its menu items and sets the location properties for each object, appropriately calculating the left and top (pixel left and pixel top in Internet Explorer) properties based on the menu's own location properties. This method implements as follows:


function setItemPositions()

{

newTop = this.top;

                newLeft = this.left + HSUBMENUGAP;

                for( i = 0; i < this.menuItemKeys.length; i++ )

                {

                               newTop = newTop + VSUBMENUGAP;

                                menuItemKey = this.menuItemKeys[i];

                                menuItem = this[ menuItemKey ];

                                menuItem.top = newTop;

                               menuItem.left = newLeft;

                                obj = getDivObj( menuItem.objID );

                                if( isIEXPLORER )

                                {

                                        obj.pixelTop = newTop;

                                        obj.pixelLeft = newLeft;

                                }

                                else

                                {

                                        obj.top = newTop;

                                        obj.left = newLeft;

                                }

                }

}



Looking back at the menu object in the sample code, inspect the implementation procedures for the other methods, paying particular attention to the implementation of the 'setMode' method. It is interesting to note how this method sets the visibility of each item depending upon the menu's mode of display.
MenuItem
'MenuItem' is the sub-node under a given menu object with the main method 'setVisible'. This method allows the visibility to be set as 'hidden' or 'visible'. The constructor for the menu item object requires a string parameter to display text and for its name, the hyper link, and the target frame name; it requires an int parameter for its index in the menu tree. The JavaScript object declaration for the menu item object is as follows:


function MenuItem( name, displayString, link, linkTarget, menuIndex  )

{

                this.name = name;

                this.displayString = displayString;

               this.menuIndex = menuIndex;

                this.link = link;

                this.linkTarget = linkTarget;

               this.top = 0;

                this.left = 0;

               this.objID = 'SubMenu' + name + menuIndex;

                this.writeMenuItemHtml = writeMenuItemHtml;

                this.setVisible = setVisible;

}



The 'writeMenuItemHtml' writes out the menu item object as a DIV element of class DIV.SUBMENU type. As mentioned earlier, this kind of style definition should be part of the header definition on the page that contains the tree menu. (This style definition will be shown later in this article.) The complete implementation of the 'writeMenuItemHtml' is as follows:


function writeMenuItemHtml()

{

                document.write( "<font size=" + FONT_SIZE + " color = " + FONT_COLOR + " FACE=\"" + FONT_FACE + "\">"  );

hrefLink =  "<div id=\""+this.objID+"\" class= \"SUBMENU\">";

               hrefLink = hrefLink + " <a href = \"" + this.link + "\"  target=" + this.linkTarget + ">" +  this.displayString + "</a> </div>";

document.write( hrefLink );

document.write( "</font>" );

}



The 'setVisible' method in menu item takes one Boolean parameter, and depending upon the Boolean value, it either sets the menu item's DIV element visibility to 'hidden' or 'visible'. The implementation is as follows:


function setVisible( blnValue )

{

                obj = getDivObj( this.objID );

                if( blnValue )

                {

                            obj.visibility = "visible";

                }

                else

                {

                            obj.visibility = "hidden";

                }

}



Notice in the object model that for each menu or menu item object referred to in JavaScript, there is a corresponding DIV element on the page. Also notice, that each menu represents a DIV element that contains an image element, and each menu item represents a DIV element on the page. In fact, the entire process was designed to represent a DIV element with the defined objects. This provides full programmatic control of all the DIV elements defined using JavaScript.

Global variables and global utility methods are important in this process. Following are a few global variables to consider:

  • VSUBMENUGAP: sets the vertical gap between the menu and submenus.
  • HSUBMENUGAP: sets the horizontal gap between a menu and its submenu.
  • VMENUGAP: sets the vertical gap between a menu and its immediate predecessor.
  • MENU_LEFT: sets the horizontal gap between a menu and the left container (browser window) border.
  • FONT_SIZE: sets the font size for the menu and menu item display strings.
  • FONT_COLOR: sets the font color for the menu and menu item display strings.
  • FONT_FACE: sets font type for the menu and menu item display strings.
  • menuTree (initially assigned a null value): is an important global variable, which is used to reference an object of type MenuTree when this object is actually created on the page script. (See example later in article.) menuTree is actually used by other methods to control the run time behavior of the navigation system.


Global methods include 'getDivObj', 'getImageObj', and 'updateMenu'. The 'getDivObj' method, as its name indicates, returns a reference to a DIV element based on that element's page ID. The menu and menu items use this method to reference their corresponding DIV objects during run time. The 'getImageObj' method returns a reference to an image object on the page based on that object's ID. Menu objects use this method to refer to their image objects on the page while swapping images. The 'updateMenu' method is an exclusive global method because it can be called from pages in other frames to update the menu tree. This is an important method because it is invoked every time a menu is clicked, so it could be said that 'updateMenu' is the main point of entry to control the menu tree.
Example
Following is an example HTML page that contains the script code to create a tree menu. To see a working example of this code, go to the following Web site:

http://members.tripod.com/~kbmurali/JavaScriptMenu




<html>

<head>

<style>

DIV.MENU{position:absolute;left:15; width:150}

DIV.SUBMENU{position:absolute; left:20; width:150}

</style>

</head>

<script language="JavaScript" SRC="javaScriptMenu.js"></script>

<body VLINK="white" BGCOLOR="black" ALINK="white" LINK="white">

<script language="JavaScript">

FONT_SIZE = 2;

menuArray = new MenuArray();

//Event log Menu

menu = new Menu( "EventLog", "EVENT LOG", "EventLogSearch.html","right",0 );

menuArray.addMenu( "EventLog", menu, 0 );

menu.setMainImage( "../Objectstore/Images/plus.gif" );

menu.setSwapImage( "../Objectstore/Images/minus.gif" );

menu.addMenuItem( "ViewEventSearch","> Search", "EventLogSearch.html","right",0 );

menu.addMenuItem( "ViewEventListing","> Listing", "EventLogListing.html","right",0  );

menu.addMenuItem( "ViewPartPtof","> ParticipantProfile",    "ParticipantProfile.html","right",0 );

menu.addMenuItem( "ViewAppList", "> ApplicationListing", "ApplicationListing.html","right",0 );

//Client Menu

menu = new Menu( "Client", "CLIENT", "ClientSearch.html", "right",1 );

menuArray.addMenu( "Client", menu, 1 );

menu.setMainImage( "../Objectstore/Images/plus.gif" );

menu.setSwapImage( "../Objectstore/Images/minus.gif" );

menu.addMenuItem( "ViewClientSearch","> Search", "ClientSearch.html","right",1   );

menu.addMenuItem( "ViewClientMaintain","> Maintain", "ClientMaintain.html","right",1);

//Participant Menu

menu = new Menu( "Participant","PARTICIPANT", "ParticipantSearch.html", "right", 2 );

menuArray.addMenu( "Participant", menu, 2 );

menu.setMainImage( "../Objectstore/Images/plus.gif" );

menu.setSwapImage( "../Objectstore/Images/minus.gif" );

menu.addMenuItem( "ViewPartSearch", "> Search", "ParticipantSearch.html","right", 2 );

menu.addMenuItem( "ViewProfile", "> Profile", "ParticipantProfile.html","right", 2 );

menu.addMenuItem( "ViewPartAppListing", "> ApplicationListing", "ApplicationListing.html","right", 2 );

menu.addMenuItem( "ViewPartEventLog", "> EventLogListing", "EventLogListing.html","right", 2  );

menu.halfModeItemsToDisplay = 3;

//Application Menu

menu = new Menu( "Application","APPLICATIONS", "ApplicationSearch.html","right", 3 );

menuArray.addMenu( "Application", menu, 3 );

menu.setMainImage( "../Objectstore/Images/plus.gif" );

menu.setSwapImage( "../Objectstore/Images/minus.gif" );

menu.halfModeItemsToDisplay = 2;

menu.addMenuItem( "ViewAppSearch", "> Search", "ApplicationSearch.html","right", 3 );

menu.addMenuItem( "ViewListOfApps", "> Listing", "ApplicationListing.html","right", 3 );

menu.addMenuItem( "ViewParticProf", "> ParticipantProfile", "ParticipantProfile.html","right", 3  );

menu.addMenuItem( "ViewAppEventLog", "> EventLogListing", "EventLogListing.html","right", 3   );

//Writing the Menu Tree

menuArray.write();

</script>

</body>

</html>

suggested improvements
This design could be improved by making the following changes:

  • Member variables could be defined for font size, color, and style in the menu and menu items instead of having these variables remain global. This would allow different menus and menu items to have different font properties.
  • In the current design, menu objects only use image objects. Menu item could be modified to facilitate using images in them.
  • Currently, the tree menu consists of only two levels of hierarchy, 'Menu' and 'MenuItem'. The design could be modified to contain multiple levels of hierarchy
  • The current tree menu fits best in a separate frame since it is placed on the top left corner. The design could be modified to allows the tree menu to be placed anywhere on the page.
conclusion
Hierarchical menus can be developed for Web page navigation using JavaScript in an object-oriented fashion; however, the developer must cleverly and carefully utilize some of the latent techniques of JavaScript and HTML to accomplish this task. In fact, this kind of design can be extended to contain multiple levels of hierarchy in the tree menu, which in turn will enable faster browsing, facilitate smoother navigation, and to give users clarity when moving from page to page within the site.
references
Flanagan, David. JavaScript: The Definitive Guide. O'Reilly & Associates, 1998
Myers, Tom. JavaScript Objects. Wrox Press Publications, 1998
Online JavaScript Reference Manual. Netscape, 2001. (http://developer.netscape.com/docs/manuals/communicator/jsguide4/index.htm)
Powell, Thomas. HTML: The Complete Reference. McGraw Hill, 2000