The JavaScript Menu Component

Creating Cross-Browser Dynamic HTML Menus

By Gary Smith


Part 1: Creating Menus


Send comments and questions about this article to View Source.
Skip to Part 2: New Features.

The JavaScript Menu component provides a user- and developer-friendly way for you to build easily customized, floating menus made up of pure, cross-browser Dynamic HTML (DHTML) and object-oriented JavaScript. In this article I'll show how you can use the (free to the public) Menu component to build DHTML menus in your own web site or web application. (Note that only version 4.0 browsers will display the DHTML menus; 3.0 browsers will simply ignore them.)

The Menu component provides the pull-down menu element that's common in most graphical user interfaces (GUIs). Using a familiar GUI element will reduce the interface learning curve of your web site or application for new users, as well as help all users more easily find what they're looking for. Having menus that contain links to sections at various levels in your web site can improve both the navigation of the site and the real estate of your web pages.

I'll show how easy yet useful it can be to take advantage of DHTML in your web site or application. Using optimized, pre-built DHTML widgets that are portable like the Menu component can save you a lot of time and effort, because the hard part has already been done for you. You only need to know how to copy the JavaScript code into your web page and how to customize it. You can try the sample scripts provided in this article and visit some of the public web sites that use the Menu component as a navigation utility (for example, the Netscape Open Studio site).

I'll assume you're familiar with the basic syntax of the JavaScript language. If you're new to JavaScript, this is a great place to learn a lot about it quickly. If you already know JavaScript, you should soon enjoy modifying and customizing your own menu objects (through an object-oriented architecture that resembles JavaBeans, or the Dialog API in the DevEdge Online sample code library).

Note that you must enable JavaScript to run these examples.

Getting Started

Adding menus to your web site is easy. To begin, you need to add the following line of code into your web page:
<SCRIPT LANGUAGE="JavaScript1.2" SRC="http://developer.netscape.com/viewsource/smith_menu/menu.js"></SCRIPT>
Here the standard HTML <SCRIPT> tag contains a SRC attribute for including the menu.js JavaScript library in your page source. This library contains the code for the Menu component constructor and functions that you'll need for building menus. It's best to place this tag in the HEAD section of your page source. As you may know, one of the many benefits of using JavaScript libraries is that they're kept in memory via Navigator's cache, so the library code doesn't have to be downloaded each time. Also, the library code can be shared by multiple pages and by different sites; when a change is made to the library file, it applies to all the pages that are sharing that code.

Adding the above <SCRIPT> tag shouldn't affect the performance of your web page. If it does in any considerable way, please report the problem. Note that you can alternatively download menu.js onto your hard drive, and point to it locally from within your code or host it on your server.

Creating Menus

The syntax for creating your own menus isn't very different from that of regular client-side JavaScript code. The Menu component provides an object-oriented application programming interface (API) which includes a collection of member functions, procedures, and variables for creating new "menu objects" -- instances of the Menu prototype object. You create a menu object by using the JavaScript new operator and the Menu() constructor, as follows:

var myMenu = new Menu();

This new expression creates a new menu object called myMenu. The template-based Menu() constructor implements prototype-based inheritance and shared properties for new menu objects. Using the new operator and a constructor is how you create most new objects in JavaScript.

You can add "menu items" to your newly created menu object with the addMenuItem() method, which is built into the Menu component. For example:

myMenu.addMenuItem("my item");

If you're familiar with the Java language, you may notice that the syntax of the Menu API is very similar to the way menus are applied to Java applications with the JDK java.awt.Menu class; the addMenuItem() method in the Menu component is analogous to the java.awt.Menu.add() method. You can add a menu item to your menu object by passing any text string as a "label" parameter to addMenuItem(). To continue adding menu items, keep calling addMenuItem(), as shown in the sample script in Example 1.


Example 1
<SCRIPT LANGUAGE="JavaScript1.2">
<!--
    var myMenu = new Menu();

    myMenu.addMenuItem("my menu item A");
    myMenu.addMenuItem("my menu item B");
    myMenu.addMenuItem("my menu item C");
    myMenu.addMenuItem("my menu item D");

    myMenu.writeMenus();
//-->
</SCRIPT>

This script creates a new menu object with four menu items, each with its own string label: "my menu item A", "my menu item B", and so on. To see what this menu object displays, click the following link, which calls the showMenu() method to display the menu:

display myMenu

I should also explain the writeMenus() method, which is called at the end of the script in Example 1. This method writes the menu GUI into the menu container of your page. The menu container is a CSS-P-based object that the Menu component creates for writing and storing your menus. The purpose of the menu container is to enable you to create and write, or change and rewrite, your menus at any time, on the fly. You need to call writeMenus() at the end of your menu script. If you instead use the write() method to write your menus at the regular window.document level after the page is loaded, you'll wind up rewriting the entire web page document. (Later the section Creating Multiple Menus illustrates using writeMenus() when you've created more than one menu.)

Moving on, Example 2 creates another, more useful menu.


Example 2
<SCRIPT LANGUAGE="JavaScript1.2">
<!--
    var myMenu2 = new Menu();
    
    myMenu2.addMenuItem("DevEdge Online", "location='http://developer.netscape.com'");
    myMenu2.addMenuItem("Open Studio", "location='http://developer.netscape.com/openstudio/'");
    myMenu2.addMenuItem("Netcenter", "location='http://home.netscape.com'");
    myMenu2.addMenuItem("Download", "location='http://home.netscape.com/download/'");
    
    myMenu2.writeMenus();
//-->
</SCRIPT>

This example is similar to Example 1, but it passes a second parameter to addMenuItem(). I've actually defined addMenuItem() to have two parameters: a "label" and an "action" parameter. The action parameter is for setting the JavaScript action or function that should be called or performed when the user clicks the menu item. In Example 2, I set all the menu item actions so that the browser will change the location of the page to the specified URL. To try this out, click the following link:

display myMenu2

Notice that like the label parameter, the action parameter is a text string, but in this case a string of code. The action string is registered by the addMenuItem() method for an event handler in the menu object, and the menu item gets a copy of the event data. The event handler performs the action at run time, when the user clicks the menu item. (The action string evaluation is similar to that performed by the JavaScript eval() method.) Wrapping the action code in a string makes it possible to pass practically any kind of action, including multiple JavaScript statements and expressions, without having the action performed until the event takes place. You can even pass variables or functions before they're defined, because they aren't evaluated until run time.

For example, instead of passing a string of code like "location='http://developer.netscape.com'" for the item action (as in Example 2), we could have passed "document.bgColor='red'" to change the document's bgColor property to red. For an example script that does this, see Example 3.


Example 3
<SCRIPT LANGUAGE="JavaScript1.2">
<!--
    var myMenu3 = new Menu();

    myMenu3.addMenuItem("Red", "document.bgColor='red'");
    myMenu3.addMenuItem("Green", "document.bgColor='green'");
    myMenu3.addMenuItem("Blue", "document.bgColor='blue'");
    myMenu3.addMenuItem("White", "document.bgColor='white'");

    myMenu3.writeMenus();
//-->
</SCRIPT>

display myMenu3

As you can see, the Menu component is very flexible, allowing you to do much more than just navigate through links.

Let's summarize the three basic steps it takes to quickly add a dynamic menu to your web page:

  1. Copy and paste the <SCRIPT> tag for adding the Menu library.
  2. Copy and paste the menu script from Example 2 (or Example 3).
  3. Copy and paste the display myMenu2 (or display myMenu3) link.

Customizing Menus

Now that you know how easy it is to add a menu to your web page, let's look at how to customize menus. You can change the sizes, fonts, and colors of your menus by changing the settings of the Menu prototype properties from their default values. Example 4 creates a menu similar to the one in Example 2, but a bit more customized.


Example 4
<SCRIPT LANGUAGE="JavaScript1.2">
<!--
    var myMenu4 = new Menu();
    
    myMenu4.addMenuItem("DevEdge Online", "location='http://developer.netscape.com'");
    myMenu4.addMenuItem("Open Studio", "location='http://developer.netscape.com/openstudio/'");
    myMenu4.addMenuItem("Netcenter", "location='http://home.netscape.com'");
    myMenu4.addMenuItem("Download", "location='http://home.netscape.com/download/'");

    myMenu4.fontColor = "blue";
    myMenu4.menuItemBgColor = "yellow";

    myMenu4.writeMenus();
//-->
</SCRIPT>

To see what this code displays, click the following link:

display myMenu4

This sample script includes two extra lines of code that override two of the default property values, by setting the menu's fontColor property to "blue" and its menuItemBgColor property to "yellow". You can copy this script and put in your own colors.

There are several other properties that you can customize in the Menu prototype object, such as the font size, the font family, and the highlight color for your menu items. Here's a list of the properties (and their default settings) that you can change:

this.fontSize = 14;
this.fontWeight = "plain";
this.fontFamily = "arial, helvetica, espy, sans-serif";
this.fontColor = "#000000";
this.bgColor = "#555555";
this.menuBorder = 1;
this.menuItemBorder = 1;
this.menuItemIndent = 15;
this.menuItemHeight = 20;
this.menuItemBgColor = "#cccccc";
this.menuLiteBgColor = "#ffffff";
this.menuBorderBgColor = "#777777";
this.menuHiliteBgColor = "#bbbbbb";
this.menuContainerBgColor = "#cccccc";
this.childMenuIcon = "images/gs.gif";

Example 5 creates the same menus as Example 4, but sets a few additional color and size properties to display a menu with a completely different look.


Example 5
<SCRIPT LANGUAGE="JavaScript1.2">
<!--
    window.myMenu5 = new Menu();
    
    myMenu5.addMenuItem("DevEdge Online", "location='http://developer.netscape.com'");
    myMenu5.addMenuItem("Open Studio", "location='http://developer.netscape.com/openstudio/'");
    myMenu5.addMenuItem("Netcenter", "location='http://home.netscape.com'");
    myMenu5.addMenuItem("Download", "location='http://home.netscape.com/download/'");

    myMenu5.fontSize = 18;
    myMenu5.fontWeight = "Bold";
    myMenu5.fontColor = "white";
    myMenu5.bgColor = "#CDB79E";
    myMenu5.menuItemBgColor = "#8B0000";
    myMenu5.menuHiliteBgColor = "#000084";
    myMenu5.menuItemHeight = 25;

    myMenu5.writeMenus();
//-->
</SCRIPT>

To see what this menu object displays, click the following link:

display myMenu5

This time the menu is brick-red, with a larger, bolder font. As you can see, the font characteristics are determined by the menu's fontWeight, fontSize, and fontColor properties. (These are the same as the regular JavaScript font styles.) Take advantage of any or all of the customizable Menu prototype properties to make your menus match the look and feel of your web site.

Creating Multiple Menus

You can add multiple menu objects and scripts to your page in various ways and display them whenever you want. Compare Example 6 to the earlier examples.


Example 6
<SCRIPT LANGUAGE="JavaScript1.2">
<!--
function loadMenus () 
{
    window.myMenu6 = new Menu("myMenu6");
    myMenu6.addMenuItem("DevEdge Online", "location='http://developer.netscape.com'");
    myMenu6.addMenuItem("Open Studio", "location='http://developer.netscape.com/openstudio/'");
    myMenu6.addMenuItem("Netcenter", "location='http://home.netscape.com'");
    myMenu6.addMenuItem("Download", "location='http://home.netscape.com/download/'");

    window.search = new Menu("Search");
    search.addMenuItem("Net Search", "location='http://home.netscape.com/escapes/search'");
    search.addMenuItem("Excite", "location='http://www.excite.com'");
    search.addMenuItem("Infoseek", "location='http://www.infoseek.com'");
    search.addMenuItem("Lycos", "location='http://www.lycos.com'");
    search.addMenuItem("Yahoo", "location='http://www.yahoo.com'");

    window.mozilla = new Menu("Mozilla");
    mozilla.addMenuItem("Mozilla.org", "location='http://www.mozilla.org'");
    mozilla.addMenuItem("Projects", "location='http://www.mozilla.org/projects.html'");
    mozilla.addMenuItem("Source Code", "location='http://www.mozilla.org/source-code.html'");

    mozilla.writeMenus();
}
//-->
</SCRIPT>

This sample script creates three menu objects: myMenu6, search, and mozilla. To see these menus, click the following links:

Notice that the three new menu objects in Example 6 are contained inside a function called loadMenus(). Placing your menu code in a function enables you to load your menus whenever you want, because the menu objects aren't actually created until the function is called. For example, you can call this function after your page is loaded, allowing the main content of your page to load the menus.

Although there are three menus, this script contains only one call to the writeMenus() method. That's a feature of the Menu component: you only need to call writeMenus() once, because every time you create a menu object for your page, the new object gets registered and stored in an array called window.menus, which contains all the menus you've created for that window. Whenever you call writeMenus() (which in turn calls document.writeln()), it writes all the menus stored in the window.menus array. This way your script calls document.writeln() only once for all the menus, rather than calling it several times, which can be much slower. For the Open Studio web site, writeMenus() takes less than one second to load its menus, and this article quickly loads about 20 example menus (hopefully you'll have far fewer menus than that!).

The window.menus array is a regular JavaScript array, so you can access the elements of it the same way you access other JavaScript arrays that you create, or the window.images or window.forms array. The window.menus array is readable and writeable; you can modify it or access the objects it contains at any time.

Also notice that Example 6 doesn't use the JavaScript var keyword like the earlier examples. Instead of using

var myMenu6 = new Menu();

this example creates a new window object property, with the following syntax:

window.myMenu6 = new Menu();

If I had instead used the var keyword, the myMenu6 object would have been declared only as a local variable of the loadMenus() function. However, I want myMenu6 to be a "global" variable (or window property), so I declared it as a property of the window object. This is one of the many powerful features of the JavaScript language.

Creating Child Menus

In this section, I'll show how you can add "child" menus, or submenus, into your regular menus to display a deeper hierarchical menu tree. Creating a child menu is exactly the same as creating a regular menu. (Again, it's similar to using the Java Menu class in the JDK.) Example 7 shows how to create a single child menu hosted inside a regular "parent" menu.


Example 7
<SCRIPT LANGUAGE="JavaScript1.2">
<!--
function loadMenus () 
{
    window.mySubMenu7 = new Menu("JavaScript");
    mySubMenu7.addMenuItem("JavaScript 1.1", "location='/eng/mozilla/3.0/handbook/javascript/index.html'");
    mySubMenu7.addMenuItem("JavaScript 1.2", "location='/docs/manuals/communicator/jsguide4/index.htm'");
    mySubMenu7.addMenuItem("JavaScript Reference", "location='/docs/manuals/communicator/jsref/index.htm'");

    window.myMenu7 = new Menu("Technologies");
    myMenu7.addMenuItem("Dynamic HTML", "location='http://developer.netscape.com'");
    myMenu7.addMenuItem(mySubMenu7);
    myMenu7.addMenuItem("Java", "location='http://home.netscape.com'");
    myMenu7.addMenuItem("Plug-ins", "location='http://home.netscape.com'");
    myMenu7.writeMenus();
}
//-->
</SCRIPT>

display myMenu7

As in the Java language, once you create a menu object, you can pass it as a child menu for another menu; Example 7 does this by passing the mySubMenu7 object as the label parameter in one of the addMenuItem() calls for the myMenu7 menu. Thus the mySubMenu7 menu object becomes the child of the parent (myMenu7) menu.

You can also share the same child menu or menus with several different parent menus, and vice versa. Example 8 shows how to create a menu that serves as the "root" parent menu for several different child menus. This script is quite short, because it uses the prescripted menu objects from the previous examples as the child menus.


Example 8
<SCRIPT LANGUAGE="JavaScript1.2">
<!--
function loadMenus () 
{
    window.myMenu8 = new Menu("my menu tree");
    myMenu8.addMenuItem(myMenu2);
    myMenu8.addMenuItem(myMenu3);
    myMenu8.addMenuItem(myMenu4);
    myMenu8.addMenuItem(myMenu5);
    myMenu8.addMenuItem(myMenu7);
    myMenu8.writeMenus();
}
//-->
</SCRIPT>

display myMenu8

Notice that the labels displayed in myMenu8 for the submenus myMenu2 through myMenu5 have the default labels "menuLabel0" through "menuLabel4". These default labels were used because no label was passed when each of those menu objects was created in the earlier examples. Compare this to Example 7, where I passed a label when creating myMenu7, by setting

window.myMenu7 = new Menu("Technologies");

Consequently, the myMenu8 menu displays "Technologies" as the label for the submenu myMenu7. If you're going to be creating submenus or adding your menus to a menu bar (as in Visual DHTML ), you should get into the habit of always passing a label to all your menu objects.

Although you can logically add as many child menus into as many parent menus as you want (to as many levels deep as you want), you should try not to use too many child menus, to avoid annoying or confusing the user. In fact, I recommend avoiding the use of child menus altogether whenever possible, although having one or two of them once in a while is usually OK. Just keep it simple.

Prototyping Menu Styles

You've seen how to customize the font and color styles of a menu object by changing the default property values, and how to create multiple customized and child menus. Usually when creating multiple customized menus for your web page, you'll want to use the same font and color styles for all your menus. For this reason, I added style prototyping into the architecture of the
Menu object.

Example 9 illustrates what you would have to do to customize the font and color styles for three sample menu objects without prototyping.


Example 9
<SCRIPT LANGUAGE="JavaScript1.2">
<!--
function loadMenus () 
{
    window.myMenu9a = new Menu("Example 9a");
    myMenu9a.addMenuItem("DevEdge Online", "location='http://developer.netscape.com'");
    myMenu9a.addMenuItem("Open Studio", "location='http://developer.netscape.com/openstudio/'");
    myMenu9a.addMenuItem("Netcenter", "location='http://home.netscape.com'");
    myMenu9a.addMenuItem("Download", "location='http://home.netscape.com/download/'");

    myMenu9a.fontColor = "#ffffff";
    myMenu9a.menuItemBgColor = "#000000";
    myMenu9a.menuHiliteBgColor = "#6699CC";
    myMenu9a.bgColor = "#AAAAAA";

    window.myMenu9b = new Menu("Example 9b");
    myMenu9b.addMenuItem("Net Search", "location='http://home.netscape.com/escapes/myMenu9b'");
    myMenu9b.addMenuItem("Excite", "location='http://www.excite.com'");
    myMenu9b.addMenuItem("Infoseek", "location='http://www.infoseek.com'");
    myMenu9b.addMenuItem("Lycos", "location='http://www.lycos.com'");
    myMenu9b.addMenuItem("Yahoo", "location='http://www.yahoo.com'");

    myMenu9b.fontColor = "#ffffff";
    myMenu9b.menuItemBgColor = "#000000";
    myMenu9b.menuHiliteBgColor = "#6699CC";
    myMenu9b.bgColor = "#AAAAAA";

    window.myMenu9c = new Menu("Example 9c");
    myMenu9c.addMenuItem("Mozilla.org", "location='http://www.mozilla.org'");
    myMenu9c.addMenuItem("Projects", "location='http://www.mozilla.org/projects.html'");
    myMenu9c.addMenuItem("Source Code", "location='http://www.mozilla.org/source-code.html'");

    myMenu9c.fontColor = "#ffffff";
    myMenu9c.menuItemBgColor = "#000000";
    myMenu9c.menuHiliteBgColor = "#6699CC";
    myMenu9c.bgColor = "#AAAAAA";

    myMenu9c.writeMenus();
}
//-->
</SCRIPT>

display myMenu9a
display myMenu9b
display myMenu9c

To change the font and color styles, I added four new lines of code to Example 6 for each of the three menu objects, for a total of 12 new lines. Imagine if there had been ten menu objects: this could add up to a lot of new code. If you want these style properties to be different for each menu object, you have no alternative; however, if you want all your menus to have the same customized look and feel, using the menu prototyping feature is faster, as well as easier to maintain. Example 10 accomplishes the same thing as Example 9 but with prototyping.


Example 10
<SCRIPT LANGUAGE="JavaScript1.2">
<!--
function loadMenus () 
{
    window.myMenu10a = new Menu("Example 10a");
    myMenu10a.addMenuItem("DevEdge Online", "location='http://developer.netscape.com'");
    myMenu10a.addMenuItem("Open Studio", "location='http://developer.netscape.com/openstudio/'");
    myMenu10a.addMenuItem("Netcenter", "location='http://home.netscape.com'");
    myMenu10a.addMenuItem("Download", "location='http://home.netscape.com/download/'");

    window.myMenu10b = new Menu("Example 10b");
    myMenu10b.addMenuItem("Net Search", "location='http://home.netscape.com/escapes/myMenu10b'");
    myMenu10b.addMenuItem("Excite", "location='http://www.excite.com'");
    myMenu10b.addMenuItem("Infoseek", "location='http://www.infoseek.com'");
    myMenu10b.addMenuItem("Lycos", "location='http://www.lycos.com'");
    myMenu10b.addMenuItem("Yahoo", "location='http://www.yahoo.com'");

    window.myMenu10c = new Menu("Example 10c");
    myMenu10c.addMenuItem("Mozilla.org", "location='http://www.mozilla.org'");
    myMenu10c.addMenuItem("Projects", "location='http://www.mozilla.org/projects.html'");
    myMenu10c.addMenuItem("Source Code", "location='http://www.mozilla.org/source-code.html'");

    myMenu10c.fontColor = "#ffffff";
    myMenu10c.menuItemBgColor = "#000000";
    myMenu10c.menuHiliteBgColor = "#6699CC";
    myMenu10c.bgColor = "#AAAAAA";

    myMenu10c.prototypeStyles = myMenu10c;

    myMenu10c.writeMenus();
}
//-->
</SCRIPT>

display myMenu10a
display myMenu10b
display myMenu10c

Compared to Example 9, this example uses only five lines of code rather than 12; the more menus there are, the more lines of code saved. This is done via the prototypeStyles property, which you set to the menu object whose styles you want to be used as the prototype for your other menu objects. View the source of the Open Studio web site to see how Open Studio uses menu style prototyping.

Displaying Menus

Remember that your menus are CSS-P-based objects that you can show and hide using the
visibility property. To make life easier for you, I've built showMenu() and hideMenu() functions into the Menu component. The showMenu() method is what I've been using all along to show the example menus. Here's how to use showMenu() as an event handler:
<A HREF="javascript://" onClick="showMenu(mozilla);">display mozilla</A>

Click the following link to see what this example displays:

display mozilla

As you can see, I set the onClick attribute for the <A> tag to call showMenu() with the mozilla menu object as its argument. This displays the mozilla menu I created earlier.

Passing a menu object as an argument to showMenu() is one of the simplest ways to show a menu. By default, the menu will be displayed wherever your mouse was when showMenu() was called: the top left corner of the menu falls at that mouse position. You can override the default menu placement as follows:

<A HREF="javascript://" onClick="showMenu(mozilla, 150);">display mozilla</A>
Click the following link to see where this example displays the mozilla menu:

display mozilla

I've defined showMenu() to have two more parameters, for setting the x and y coordinates on the page (for the left and top positions) where you want the menu to be displayed. The above example shows how to override the default x position: passing 150 as the x coordinate displays the mozilla menu exactly 150 pixels from the left page border, rather than where the mouse pageX position was. An onClick attribute of "showMenu(mozilla, 150, 150)" would set the x and y coordinates equally.

You can also use other event handlers and actions to show and hide menus. Here's how to use the familiar onMouseOver event handler to display menus:

<A HREF="javascript://" onMouseOver="showMenu(mozilla);">display mozilla</A>
This is the same as the previous example, except that it uses onMouseOver instead of onClick to show the menu. Run your mouse over the following link to see how this works:

display mozilla

You may prefer using onMouseOver instead of the onClick style for displaying menus on your web page. I recommend implementing the onClick style for application interfaces, because most application GUIs display menus based on user clicks. However, for web pages the onMouseOver style is well accepted in most cases, as in the Open Studio site. Again, just remember to keep it simple.

You can implement the onMouseOver or onClick interface with an image, too, or use other event handlers, or capture any of the new JavaScript 1.2 events, such as DblClick, DragDrop, KeyDown, KeyPress, KeyUp, MouseDown, MouseMove, MouseOut, MouseOver, MouseUp, Move, or Resize.

You can also pass the menu's label string or CSS-P object ID to showMenu() -- the Menu component looks for these variables, too. Compare the following with the previous example:

<A HREF="javascript://" onMouseOver='showMenu("Mozilla");'>display mozilla</A>

display mozilla

See the difference? The argument passed to showMenu() is the "Mozilla" label (a string type) rather than the mozilla object type. This flexibility enables you to create event handlers more easily (especially if you're not so familiar with object-oriented syntax) and in a greater variety of ways.

On the subject of hiding menus: Usually you don't have to worry about doing this, because the Menu component sets up the hideMenu() method to hide the menus for you, by default. That is, when the user moves the mouse out of a menu, hideMenu() gets called, because I set the onMouseOut event handler to hideMenu() for the menu objects. I also overrode the window.onMouseUp event handler with hideMenu() to hide the menu if the user clicks elsewhere in the window. This reflects the menu behavior that's familiar on most operating systems. If you want to change any of this default behavior, you can override these event handlers with your own functions.

Sharing Menus Across Domains

You can really take advantage of the basic features of the Internet with JavaScript, especially when you use JavaScript libraries. Storing your menu code in JavaScript
.js libraries allows you not only to share code on your own domain, but also to safely share it across different domains, similar to how folks have shared GIF images since the beginning of the web's existence. In other words, you can view the prototypical Menu library as a real Internet component or shared object.

Open Studio is a perfect example of this. It not only uses all the Menu component features that I've discussed so far, but it also distributes the Menu component and several customized menu objects across the Netscape developer domain, as well as other external domains such as the C|NET, Wired, Adobe, Apple, and Macromedia domains, just to mention a few.

The way Open Studio shares its menu objects is by storing them in a JavaScript library named openstudio.js. (This library also contains other code that both Open Studio's pages and other external sites share, such as some links and MouseOver code.) As illustrated in previous examples, the openstudio.js library wraps the menu objects inside a function called loadMenus(), so that the menu objects can be created after the main web page content is laid out. Like the scripts in this article, the openstudio.js library depends on the menu.js library for accessing the Menu code. Sharing the code of these two libraries allows Open Studio to share its menu objects (and other code) across the various web pages and domains.

If you like menus, you really should use Open Studio as a reference. It shows how useful menu objects can be, and how well they work on a busy web site -- and they're very customized (white letters on a black background, with blue as the highlight color, to match the site).

Cross-Browser Compatibility

Netscape Communicator 4 and Microsoft Internet Explorer 4 support different document object models (DOMs). The
Menu component accommodates for these kind of differences. Look at the Menu source to see exactly how this is done. Notice that I didn't use any of the traditional "browser sniffing" techniques. Instead of looking for numerous browser versions and names, I used "object sniffing," which is a lot simpler.

Let's look at how object sniffing works in the Menu component. For situations where I know there is (or could be) a difference in the browser DOMs, such as with the document.layers vs. document.all array objects, I use something like the following code:

if (document.layers) 
{
    document.layers["myObjectID"].top = 100;
}
else if (document.all)
{
    document.all["myObjectID"].styles.pixelTop = 100;
}

Object sniffing can be more reliable than browser sniffing. However, if you're using a browser sniffer, don't make common mistakes like if (version == 4.0) or even if (version == 4), in case a 4.1 or 5.0 browser comes out a few months later. Use if (version >= 4) instead to allow for future versions.

An earlier (mid-1997) version of the Menu component is even more flexible and robust (see also other objects for Visual DHTML). However, this earlier version is more for web applications than web sites, and it doesn't run on Internet Explorer 4, since that IE version wasn't around at the time.

Known Issues

One of the main features of the Menu component is cross-browser compatibility; however, both Communicator 4 and Internet Explorer 4 are known to sometimes fall short in different areas regarding their new DOMs and the DHTML "draft standards." The IE implementation of JScript is getting better, but it's still virtually light years behind the standard implementation of JavaScript 1.2 and LiveConnect in Communicator. On the other hand, IE4 seems to hold more water regarding the HTML 4.0 draft, because it came out later than Communicator; however, so far it's still less stable. Keep in mind that the upcoming 5.0 browsers will support DHTML much better and will conform to the W3C final specs, which until now have been a moving target (Netscape, at least, is committed to supporting all the W3C standards).

Currently, a weaker area for Communicator 4.0 is the layout: when you resize the window it sometimes loses some of the properties for CSS-P objects (the <LAYER> tag is still more robust). To work around this problem, you can capture the Resize event and call a function to reload the menu or page.

IE4 has a layout bug, too: you'll need to load your menus before the window's onLoad event handler is called, because IE4 will most often crash when the Menu component tries to use the JScript innerHTML property for CSS-P objects after the page is laid out, especially with multiple menus. Load your menus early and you should be fine.

You'll also often need to specify the width of your menus for IE4, because IE4 doesn't explicitly assign values to CSS-P object properties. When you create CSS-P objects the default behavior usually isn't what you think it should be (especially the pixelWidth property), so you'll need to specifically set each property. To work around the menu width problem in particular, I set the default pixelWidth in the Menu component to 200 pixels for IE4; otherwise, your menus would always be the width of your whole web page document in IE4.

The DOM in Communicator handles default behavior much better, because it assigns default values to virtually all CSS-P properties for your objects. In this case your menus will always be the width of your largest menu item. Note that the Menu component allows you to specify the width of your menus for both browsers by setting the menuItemWidth property.

Going Forward

I plan to update and improve the Menu component based on feedback from developers, so please send in your suggestions, requests, or comments regarding this component. If your site uses the Menu component, you can submit your URL for possible posting on the Sample Menus page.


See also Part 2: New Features


Further Resources


View Source wants your feedback!
Write to us and let us know
what you think of this article.


Many thanks to Paul Dreyfus, Andres Espineira, and Richard Hall for the opportunity to write this article, and special thanks to Caroline Rose for the excellent editing job.

Gary Smith is a Netscape engineer, creator of Visual DHTML, Dialog Widget, and Webtop Widget. He works on new technology projects involving LiveConnect, JavaScript, Java, CGI, XML/RDF, and web-crawling technologies. Occasionally he writes sample code and documentation for DevEdge Online, such as the View Source article Connecting JavaScript to Java with LiveConnect.


DevEdge Online FAQ
Developer Response Center
Join DevEdge Program
Copyright © 1998 Netscape Communications Corporation.
This site powered by: Netscape Enterprise Server and Netscape Publishing System 1.6.