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).
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:
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:
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>
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:
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.
To see what this code displays, click the following link:
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>
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: 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.
To see what this menu object displays, click the following link: 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 <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>
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.
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>
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>
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.
Example 9 illustrates what you would have to do to customize the font and
color styles for three sample menu objects without prototyping.
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 <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 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.
display myMenu9b
display myMenu9c
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.
Click the following link to see what this example displays: 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>
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:
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:
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>
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.
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.