Categories
JavaScript

Implement a DHTML Mouseover effect with DOM

Originally published in WebBuilder magazine. Found courtesy Wayback Machine.

The DOM, or Document Object Model, is a specification recommended by the World Wide Web Consortium (W3C) primarily to help eliminate cross-browser dynamic HTML differences. It is implemented with Microsoft’s Internet Explorer (IE) 5.x, and will be implemented with Netscape’s Navigator 5.x when it is released. You probably haven’t seen that many demonstrations of the DOM and its impact on DHTML implementations, and the ones you have seen probably have been fairly complicated. At times you might even think it would be less complicated and would require a lot less code to implement the DHTML using the technologies supported in the 4.x browsers and just deal with the cross-browser problems.

However, you will find that in the long run, the DOM, in addition to XML (Extensible Markup Language), HTML 4.0, and CSS (Cascading Style Sheets), will simplify your development efforts once you have grown accustomed to the technology. In fact, using the DOM can actually make your coding a whole lot easier and cut down on the number of lines of code you need, depending on what you hope to accomplish.

This article will show you how to create a text-based menu mouseover effect, complete with menu tips that will work with IE 5.x and with the August, 1999 M9 build of Gecko available at Mozilla.org (as tested in a Windows environment). Before learning how to use the DOM specification to create a mouseover effect, you might find it useful to get a little history on mouseovers as they are implemented without using the DOM. This next section will highlight why the DOM is an improvement over the existing implementations of DHTML.

Pre-DOM Mouseover Effects
One of the first implementations of “dynamic” HTML occurred when Netscape exposed images for access from an images array from the HTML document object, and then allowed you to modify the source of the image through the src attribute. For instance, this line of code uses JavaScript to replace the existing source of a displayed image with a new image source:


document.images[0].src = "somenew.gif";

A popular use of this dynamic HTML technique was to implement the mouseover effect. The mouseover effect gives a visual cue to the user that the mouse’s cursor is over a certain element in a Web page. The cue remains visible until the cursor moves away from the element. The mouseover effect has long been considered one of the classic implementations of dynamic Web page effects.

Most commonly, you use mouseover effects to highlight menu items. A problem with using the image changing approach for this purpose is that you have to use graphics for the menu, adding to the overall page download times, and the effect won’t work with anything but images. If you wanted to provide a help message for the menu item, you would need to include this message as a part of the image or use some other technique such as Java applets.

These limitations were resolved when CSS positioning and styles, and exposure of the browser document object model, were released under the term of “Dynamic HTML” (DHTML) in Microsoft’s Internet Explorer 4.x and Netscape Navigator 4.x. With the introduction of DHTML, changing the image source wasn’t the only approach you could take to generate a mouseover effect. You could use dynamic positioning, including hiding and showing elements to display the associated menu item text.

This example shows a menu item with a hidden menu text tip. By capturing the onMouseOver and onMouseOut event handlers, you change the style of the menu text to show the tip when the mouse is over the menu item; otherwise you return the text to its original appearance to hide the tip:


<DIV id="one" style="width: 150; z-index: 1" 
   onmouseover="this.style.color='red';onetext.style.visibility='inherit'"
   onmouseout="this.style.color='black';onetext.style.visibility='hidden'">
Menu Item One
</DIV>
<DIV id="onetext" style="visibility: hidden; margin: 20px">
This is the associated text for Menu Item One
</DIV>

However, this approach did not work as intended because the implementation of DHTML included with the 4.x browsers only supported element hiding when the element was positioned. Also, the style setting would not work with Navigator 4.x. Navigator 4.x does not allow you to modify the script of an element’s CSS1 style setting after the element has been rendered (displayed) to the page.

To get around the cross-browser limitations and differences, you could create two different absolutely positioned versions of the elements, and hide one of them. The hidden element would then have the “highlighted” CSS style setting and would be shown when the mouse was over the element and hidden otherwise:


<DIV id="one1" style="z-index: 1"
   onmouseover="switch_on('one')">
Menu Item One
</DIV>
<DIV id="one2" style="color: red;
   font-weight: 700; z-index: 2; visibility:hidden"
   onmouseover="switch_off('one')">
Menu Item One <br>
This is the associated help text to display with menu item one
</DIV>

This approach again worked with IE, but not with Navigator, because Navigator and IE supported different event models and event handlers. To make sure event handling worked with both browsers, and to be consistent, you would use a link to surround the menu item and the mouse events would be captured in the link:


<a href="" onclick="return false" onmouseover="switch_on('one')">

With this workaround, the mouse events are being captured correctly, but there’s still one more problem remaining, which I call the “phantom mouseover effect.” Normally, a user moves the mouse cursor over an element, triggering the process to hide the regular menu item and show the highlighted version. When the user moves the mouse cursor away, the effect is reversed. However, if the person moves the mouse too quickly, the original element gets both the mouseover and mouseout events before the highlighted menu item is even shown. When this happens, the highlighted element stays visible even when the mouse is moved out of the area because it didn’t receive the mouseout event, leaving what is virtually a phantom effect. The user must move the mouse’s cursor over the item again, more slowly, to trigger the regular menu item to appear.

To avoid this phantom effect, you can employ another technique that uses a third, invisible element. In this case, you use a small transparent GIF image and size it to fit over the menu item. The invisible element traps both the mouseover and mouseout events, and invokes the functions to hide the regular and highlighted menu items accordingly. Here is an example of this type of mouseover handling that works with Navigator 4.x and up and IE 4.x and up. First, you create the menu item, its highlighting, and the event capturing blocks:


<!-- menu item one -->
<DIV id="one" style="left: 140; top: 140; z-index: 2">
<a href="" onclick="return false" 
   onmouseover="switch_on('one')"
   onmouseout="switch_off('one')"><img src="blank.gif" 
width=150 height=30 border=0></a>
</DIV>

<DIV id="oneb" style="left: 150; top: 150;
   z-index: 1">
Menu Item One
</DIV>

<DIV id="onec" style="left: 150; top: 150; 
   z-index: 1; visibility:hidden"
   class="highlight">
Menu Item One -
This is the associated help text to display with menu item one
</DIV>

Next, you create the script that processes the menu highlighting:


// set items visibility using 
// specific browser technology
function SetObjVisibility (obj, visi) {
   if (navigator.appName == "Microsoft Internet Explorer")
        document.all(obj).style.visibility=visi;
   else
        document.layers[obj].visibility=visi;
}

// switch highlighting on
function switch_on(theitem) {
   SetObjVisibility(theitem+"b", "hidden");
   SetObjVisibility(theitem+"c","inherit");
}

// switch highlighting off
function switch_off(theitem) {
   SetObjVisibility(theitem+"c", "hidden");
   SetObjVisibility(theitem+"b","inherit");
}

To overcome cross-browser document object model differences, you use an eval function to evaluate and invoke the visibility setting for the element being hidden or displayed. This page will work with Navigator 4.x and up and IE 4.x and up. However, the workarounds to the cross-browser problems make the code much larger and more complex than you’d want for such a simple effect. Instead, you should consider using the DOM to create a simple mouseover menu effect.

Enter the DOM
DOM Level 1 is the most recent recommended specification for DOM from the W3C. The DOM supports a browser-neutral specification that, when implemented within a browser, lets you dynamically access the elements within the Web page, using an approach that will work consistently across browsers and across platforms.

Without getting into too much detail on the DOM, the specification groups the elements of a Web page into a hierarchy, and you can obtain a reference to an element by first accessing its parent and then accessing the element from the parent’s element list. For instance, an HTML table would contain rows, the rows would contain cells, and the cells would contain the data that is displayed. To access a specific cell’s data, you would first need to access the table, then the row containing the cell, the cell, and then access the cell’s contents.

Another key aspect to the DOM is that instead of defining every single HTML element within the specification, it defines a fairly generic set of elements and then defines how to work with the elements directly, and as they relate to each other. Additionally, the W3C has provided an ECMAScript binding for the core elements of the DOM, and the HTML-specific API based on the DOM.

The example in this article uses the HTML version of the document object, or HTMLDocument. This version provides a method, “getElementById”, which allows you to access an element within the document by its “ID” attribute. Additionally, Navigator 5.x and IE 5.x both support HTML 4.0 and CSS2 (for the most part), which means both support the onmouseover and onmouseout event handlers within tags such as DIV tags. Also, both browsers expose the style object so you can dynamically modify the CSS style attribute of an element. Here, you define the two menu items and their associated menu tips:


<!-- menu item one -->
<DIV id="one" style="height: 30; width: 140"
   onmouseover="on('one')" onmouseout="off('one')">
Menu Item One
</DIV>

<DIV id="onetext" 
   style="display:none; width: 140; margin: 10px; 
   font-size: 10pt; background-color: white; color: red">
This is the text associated with the first menu item
</DIV>

<!-- menu item two -->

<DIV id="two" id="two" style="height: 30; width: 140"
   onmouseover="on('two')" onmouseout="off('two')">
Menu Item Two
</DIV>
<DIV id="twotext"
   style="display:none; width: 140; margin: 10px; 
   font-size: 10pt; background-color: white; color: red">
This is the text associated with the second menu item
</DIV>

Because you define the menu items as DIV blocks that are not absolutely positioned within the Web page, they will appear in the upper left corner of the document. Also, notice that the menu tips aren’t hidden with the visibility property; you remove them out of the context of the document with the display CSS attribute set to “none”.

Next, you create the script that processes the menu highlighting. This script does a couple of things. First, it uses the type attribute for the SCRIPT element to define the language used for the script block.


<SCRIPT type="text/JavaScript">

Then the script creates functions to highlight the menu item (“on”) and turn off highlighting (“off”). The functions themselves access the menu item and tip by using the DOM method getElementById. This method returns a reference to the element you want to modify:


// get specific div item, identified by node index
var itm = document.getElementById(val);
var txt = document.getElementById(val+"text");

The functions turn the display for the menu tip on or off, depending on whether the mouse is over the menu or has moved away from the menu item. Because you use display instead of visible, the other elements of the page are moved to accommodate the newly displayed item. Visible hides an element but leaves the “box” that the element occupies within the document flow; display set to “none” removes the element completely from the page flow:


// turn on menu tip display
txt.style.display="block";

…

// turn off menu tip display
txt.style.display="none"

In addition to altering the display of the menu tip, you can also change the CSS style on the menu item. For example, you can increase the font weight and modify the font and background color of the element. Notice that no cross-browser code is present in this example. With the 5.x releases of Navigator (as demonstrated in the M9 release of Gecko that you can obtain at Mozilla.org) and IE, both browsers now support exposing CSS attributes through the style object and dynamically modifying these attributes:


// set style properties
itm.style.backgroundColor="green";
itm.style.color="yellow"
itm.style.fontWeight = 700;

By using the DOM (and browsers that support HTML 4.0 and CSS), you can halve the amount of code required to create the mouseover effect, as you can see from the complete example.

Categories
JavaScript

Adding Persistence to DHTML Effects

Originally published at Netscape Enterprise Developer, now archived at the Wayback Machine

Dynamic HTML (DHTML), implemented in Netscape Navigator 4.x and Microsoft Internet Explorer 4.x, gives the Web page visitor the ability to alter the position, format, or visibility of an HTML element. However, the effects that are created with DHTML are transitory in that the next time the page is refreshed, the current DHTML state is not maintained and the page opens showing the same content layout as when the page is first loaded. Any changes to the content based on DHTML are not maintained. Sometimes this isn’t a problem, but other times this is irritating to the reader. This article covers how to add persistence to a DHTML page. Examples should work with all forms of Netscape Navigator 4.x and Internet Explorer 4.x, but have only been tested with IE 4.x and Netscape Navigator 4.04 in Windows 95.With DHTML, developers can provide functionality that hides or shows whichever layer the Web page reader wants to view. Developers can also add functionality that lets readers move content. The problem is that DHTML effects are not session persistent, which means that they aren’t maintained between page reloads. Following a link to another site and returning to the page can trigger a reload, which wipes out the effect and annoys the user,. especially if he or she has spent considerable effort achieving the current DHTML state.

For example, you can use DHTML to let your reader position your page contents for maximum visibility in her browser and get the page look just right. If she decides to leave your site to check Yahoo’s news for a few minutes and then comes back, her settings will have been erased.

So, what’s the solution to these problems? It’s relatively simple — add document persistence using Netscape-style cookies.

Using cookies 
Despite the name, you won’t find Netscape-style cookies at your local bakery. Cookies are bits of information in the form of name-value pairs that are stored on the client’s machine for a set period of time or until the browser is closed. For security, cookies are created and accessed using specific syntax, are limited to 300 cookies total within the cookie database at 4K per cookie, and are limited to 20 cookies per domain (the URL where cookie was set).

Cookie syntax consists of a string with URL characters encoded, and may include an expiration date in the format:

Wdy, DD-Mon-YY HH:MM:SS GMT

Netscape has a complete writeup on cookies (see our Resource section at the end), including functions that can be copied and used to set and get cookies. I created modified versions of these functions to support my DHTML effects. As I don’t want to add to overly burdened cookie files, I don’t set the expiration date, which means the cookie does not get stored in the persistent cookie database or file. This also means that the DHTML effect only lasts for the browser session. However, this fits my needs of maintaining a persistent DHTML state in those cases where the reader moves to another site or hits the reload button.

DHTML state cookie functions
I created a JavaScript source code file called cookies.js that has two functions. One function sets the cookie by assigning the value to the document.cookie object. More than one cookie can be set in this manner, as cookie storage is not destructive — setting another cookie does not overwrite existing cookies, it only adds to the existing cookie storage for the URL. The cookie setting function is shown in the following code block:

// Set cookie name/value
//
function set_cookie(name, value) {
   document.cookie = name + "=" + escape(value);
}

Next, the function to get the cookie accesses the document.cookie object and checks for a cookie with a given name. If found, the value associated with the cookie name is returned, otherwise an empty string is returned. The code for this function is:

// Get cookie given name
//
function get_cookie(Name) {
  var search = Name + "="
  var returnvalue = "";
  if (document.cookie.length > 0) {
    offset = document.cookie.indexOf(search)
    if (offset != -1) { // if cookie exists
      offset += search.length
      // set index of beginning of value
      end = document.cookie.indexOf(";", offset);
      // set index of end of cookie value
      if (end == -1)
         end = document.cookie.length;
      returnvalue=unescape(document.cookie.substring(offset, end))
      }
   }
  return returnvalue;
}

That’s it to set and get cookie values. The next section shows how to create two Web pages with simple, cross-browser DHTML effects. Then each page is modified to include the use of cookies to maintain DHTML state.

_BREAK1 Creating the DHTML Web pages
To demonstrate how to add persistence to DHTML pages, I created two Web pages implementing simple DHTML effects. The first page layers content and then hides and shows the layers based on Web-page reader mouse clicks. The second page has a form with two fields, one for setting an element’s top position and one for setting an element’s left position. Changing the value in either field and clicking an associated button changes the position of a specific HTML element.

Adding DHTML persistence for layered content
For the first demonstration page, a style sheet was added to the top of the page that defines positioning for all DIV blocks, formatting for an H1 header nested within a DIV block, and three named style sheet classes. The complete style sheet is shown here:

<STYLE type="text/css">
        DIV { position:absolute; left: 50; top: 100; width: 600 }
        DIV H1 { font-size: 48pt; font-family: Arial }
        .layer1 { color: blue }
        .layer2 { color: red }
        .layer3 { color: green }
</STYLE>

Next, three DIV blocks are used to enclose three layers, each given the same position within the Web page. Each block also contains a header (H1), with each header given a different style-sheet style class. The HTML for these objects is:

<DIV id="layer1">
<H1 class="layer1">LAYER--BLUE</H1>
</DIV>
<DIV id="layer2" style="visibility:hidden">
<H1 class="layer2">LAYER--RED</H1>
</DIV>
<DIV id="layer3" style="visibility:hidden">
<H1 class="layer3">LAYER--GREEN</H1>
</DIV>

That’s it for the page contents. To animate the page, I created a JavaScript block that contains a global variable and two functions. The global variable maintains the number of the layer currently visible. The first function is called cycle_layer; this function determines which layer is to be hidden and which is shown next, and then calls a function that performs the DHTML effect:

<SCRIPT language="javascript1.2">
<!--

// current layer counter
current_layer = 1;

// assign document clicks to function pointer
document.onclick = cycle_layer;

// cycle through layers
function cycle_layer() {
   var next_layer;
   if (current_layer == 3)
        next_layer = 1;
   else
        next_layer = current_layer + 1;
   switch_layers(current_layer, next_layer);
   current_layer = next_layer;
}

The scripting block also assigns the function cycle_layer as the event handler function for all mouse clicks that occur within the document page. By doing this, clicking any where in the page document area that doesn’t include any other content results in a call to the function to change the layer.

The switch_layers function is the DHTML effects function, and it uses the most uncomplicated technique to handle cross-browser differences: it checks for browser type and then runs code specific to the browser. Other techniques can be used to handle cross-browser differences, but these are outside of the scope of this article. All that the function does is hide the current layer and show the next layer in the cycle, as shown here:

// hide old layer, show new
function switch_layers(oldlayer, newlayer) {
   if (navigator.appName == "Microsoft Internet Explorer") {
        document.all.item("layer" + oldlayer).style.visibility="hidden";

        document.all.item("layer" +
newlayer).style.visibility="inherit";
        }
   else {
        document.layers["layer" + oldlayer].visibility="hidden";
        document.layers["layer" + newlayer].visibility="inherit";
        }
}

Try out the first sample page. Clicking on the document background, not the text, changes the layer. Try setting the layer to the green or red layer, accessing another site using the same browser window, and returning to the sample page. When you return, the page is reset to the beginning DHTML effect, showing the blue layer.

To correct the loss of the DHTML effect, we can add persistence to the page by storing which layer is showing when the page unloads. This information is captured in a function called when the onUnLoad event fires.

To add persistence, I added the cookies Javascript source code file to the page, using an external source code reference:

<!-- add in cookie functions -->
<SCRIPT language="javascript" src="cookie.js">
</SCRIPT>

Next, I added the function to capture the DHTML state:

// onunload event handler, capture "state" of page (article)
function capture_state() {
   set_cookie("current_layer",current_layer);
}

When the page reloads, the onLoad event is fired, and a function is called from this event to “redraw” the DHTML effect. The function is called start_page, and it pulls in the cookie containing which layer should be shown:

function start_page() {
// get cookie, if any, and restore DHTML state
  current_layer = get_cookie("current_layer");
  if (current_layer == "")
        current_layer = 1;
  else
        switch_layers(1, current_layer);
}

Finally, the two event handlers, onUnLoad and onLoad, are added to the BODY HTML tag:

<BODY onload="start_page()" onunload="capture_state()">

You can try out the second sample page, which has DHTML persistence — again, change the layer, open some other site and then return to the sample page. This time, the DHTML effect persists during a page reload.

Sometimes an effect requires more than one cookie, as the next example demonstrates.

_BREAK2 Adding DHTML Persistence for Positioned Content
In the next example, I used a style sheet again to define CSS1 and CSS-P formatting for the DIV block that is going to be moved in the page:

<STYLE type="text/css">
        DIV { position:absolute; left: 50; top: 100; width: 600 }
        DIV H1 { font-size: 48pt; font-family: Arial }
        .layer1 { color: blue }
</STYLE>

Next, I created a form that has two text elements and associated buttons. One text and button element pair is used to change the DIV block’s top position, one pair is used to change the DIV block’s left position. The entire form is shown here:

<DIV id="first" style="position:absolute; left: 10; top: 10; z-index:2">
<form name="form1">
Set new Left Value: <input type="text" name="newx" value="50">
<input type="button" onclick="newleft(newx.value)" value="Set New
Left"><p>
Set new Top Value: <input type="text" name="newy" value="100">
<input type="button" onclick="newtop(newy.value)" value="Set New Top">
</form>
</DIV>

Next, the positioned DIV block is created:

<DIV id="layer1" style="z-index: 1">
<H1 class="layer1">LAYER--BLUE</H1>
</DIV>

Once the page contents have been created, you can add code to animate the DIV block based on whether the top or left position is changed. The function to change the top position is:

// change top value
function newtop(newvalue) {
   if (navigator.appName == "Microsoft Internet Explorer")
        layer1.style.pixelTop = parseInt(newvalue);
   else
        document.layer1.top = newvalue;
}

Again, the least complex technique to handle cross-browser differences is used, which is to check for the browser being used and run the appropriate code. The function to change the left position is identical to the top position function, except the CSS-P attribute “left” is changed rather than the CSS-P attribute “top”:

// change left value
function newleft(newvalue) {
   if (navigator.appName == "Microsoft Internet Explorer")
        layer1.style.pixelLeft = parseInt(newvalue);
   else
        document.layer1.left = newvalue;
}

That’s it for this page. You can try it out on the third sample page, where you can change the position of the DIV block by changing the left position, the top position, or both. Again, open another site in the browser and then return to the example page. Notice how the position of the DIV block does not persist between page reloadings.

Adding persistence to this page is very similar to adding persistence to the layered example page, except two values are maintained: the top and left values. The start_page and capture_state functions for this DHMTL effect are:

function start_page() {
// get cookie, if any, and restore DHTML state
  var tmpleft;
  tmpleft = get_cookie("currentleft");
  if (tmpleft != "") {
        currentleft = parseInt(tmpleft);
        currenttop = parseInt(get_cookie("currenttop"));
        newtop(currenttop);
        newleft(currentleft);
        if (navigator.appName == "Microsoft Internet Explorer") {
           document.forms[0].newx.value=currentleft;
           document.forms[0].newy.value=currenttop;
           }
        else {
           document.first.document.forms[0].newx.value = currentleft;
           document.first.document.forms[0].newy.value = currenttop;
           }
        }
}

function capture_state() {
        set_cookie("currentleft", currentleft);
        set_cookie("currenttop", currenttop);
}

To see how it works, try out the fourth sample page, move the DIV block, open another Web site in the browser and return to the example page. This time the DHTML effect does persist between page reloadings.

Summing up
In a nutshell, the steps to add persistence to a page are:

  1. Create the DHTML page
  2. Determine the values that must be maintained to re-create an existing effect
  3. Add the Netscape-style functions to the page
  4. Capture the onUnLoad event and call a function to capture the DHTML effect persistence values
  5. Capture the onLoad event and call a function to restore the DHTML effect from the persisted values

Using Netscape-style cookies to maintain DHTML effects won’t help with some DHTML persistence problems. For example, DHTML effects can be lost when a page is resized. In addition, if a page is resized between the time the DHTML persistence values are stored and the page is reloaded, the values may no longer work well with current page dimensions. However, for most effects and for most uses, the use of Netscape-style cookies is a terrific approach to DHTML persistence.

Categories
JavaScript Technology

Using Dynamic HTML to create an animated menubar

Originally published in Netscape World, now archived at the Wayback Machine

Microsoft and Netscape decided to use two different techniques for Dynamic HTML. Microsoft bases its approach on exposing CSS1 (Cascading Style Sheets) attributes to script, with a little help from some interesting new built-in, lightweight, and windowless ActiveX controls. Netscape bases much of its approach on the new LAYER tag.

At first glance, the approaches seem incompatible — using one browser’s techniques will not work with the other. However, you can use both browsers’ techniques within the same page and generate the exact same result.

In this article, I create an animated menu bar using a combination of CSS1 style sheets, the PATH ActiveX control, and dynamically modifying HTML elements to work with IE 4.0. I create the exact same effect using CSS1 style sheets and the LAYER tag for Navigator 4.0. Note, though, that both IE 4.0 and Navigator 4.0 are works in progress, and this example may need to be modified, slightly, for future versions of either product.

Creating style sheetsThe first section of the example page is the CSS1 style sheet settings. Both IE 4.0 and Navigator 4.0 process these style sheets, though Navigator does not seem to like setting images using the IMG tag at this time.

This example image uses headers for the text-based portion of a menu bar. To create the effect I want, I don’t want the standard underline that occurs with hypertext links, and I do want to modify the font size and color. As I don’t want either of these changes to impact on the rest of the document, I use the cascading effect of CSS1 to apply these changes only to links contained within <H2> header tags:

h2 A { text-decoration: none; color: gold } 
h2 A:link { color: gold } 
h2 A:visited { color: gold }

Next, I create the style sheet definitions for the menu images, for use with IE 4.0 only. I use absolute positioning to place the images exactly where I want on the page, and use the visibility property to hide the images when the page is first opened:

#menu1 { position: absolute; left: 0; top: 0; 
	visibility: hidden}
#menu2 { position: absolute; left: 142; top: 0; 
	visibility: hidden}
#menu3 { position: absolute; left: 284; top: 0; 
	visibility: hidden}
#menu4 { position: absolute; left: 192; top: 0; 
	visibility: hidden}
#menu5 { position: absolute; left: 142; top: 192;
	visibility: hidden }
#menu6 { position: absolute; left: 284; top: 192; 
	visibility: hidden}

The final part of the CSS1 style sheet definition creates the text-based menu components of the menu bar. These show as soon as the page is opened, and if the Web page reader does not want to wait for the images, or has image downloading turned off, they can access the links via the text menu. The size of the area for the menu text item should be large enough to contain the complete text, otherwise odd behavior will result with Internet Explorer: It does not display the text correctly when you click on a text-based menu item. Another text-based style sheet rule is provided for a message that is displayed while the images are loading. Whenever you create content that may be invisible or not displayed for a time, provide some kind of feedback to the user about what is happening:

This is the final section of the CSS style sheet definitions.

Adding images to a Web pageThe next section in the example page is the scripting, but I will talk about that a bit later. For now, I will add the images to the Web page using two different techniques, one for IE 4.0, and one for Navigator 4.0. IE 4.0 uses the style sheet settings defined for the images, and references the appropriate definition by using the identifier of the style sheet rule. The Navigator technique is to embed the images within layers that are hidden when the page first loads. IE 4.0 ignores the LAYER tag, and Navigator ignores the image style sheet rules:

Note that a hypertext link reference is applied to the image, and the border is set to 0. As the image style sheet rules seem to be ignored by Navigator, important image information such as widthheight, and border are specified within the image tag.

Next, the text objects are added to the menu bar. This includes a text-based alternative that labels each image when it is displayed, or can be accessed directly if the image is not displayed. This also includes a message stating that the images for the menu bar are being loaded:

The “waiting…” text is placed within a LAYER tag as I need to add script to modify it later, and only layer-based elements can be modified with Navigator at this time.

The final objects to add to the page are six ActiveX PATH objects, to control the menu bar animations for IE 4.0. A path object can be used to specify both X and Y coordinates at specific tick marks and moves the visual object associated with the control along that path. Two of the parameters for the control are the X path coordinates and the Y path coordinates. Another parameter could also control the timer, but I want to synchronize my image movement, so I use a different script-based approach later:

Notice that the X, Y coordinates for each path object are different, but all of them have the same number of timing ticks, which is the first parameter given in each of the coordinate pairings. The tick timings given are in microseconds.

The PATH ActiveX controls are the last elements placed into the page. Next comes the fun part, animating the menu bar.

Adding the script to animate the menu barThe animation is started when the page loads by trapping the onLoad event for the document. This calls a function called cycle. This function is implemented in both VBScript and JavaScript, and the appropriate browser picks the appropriate implementation, with Microsoft grabbing the VBscript version, Navigator grabbing the JavaScript version.

The first script controls the menu bar for IE 4.0. This is written entirely in VBScript, though JavaScript could be used if browser differences are handled correctly. The cycle function sets the Target properties of the path objects to the style sheet rules of the menu bar images. This associates each path control with its visual object. Next, the images are set to be visible, and the “waiting..” text is hidden.

The Play method for each path control is called, beginning the animation process, though no movement occurs yet. Why? No movement occurs because no timing mechanism has been associated with each control. The timing is handled by using the setTimeout function and calling a second subroutine that handles the image animation. The complete text of the cycle subroutine is here:

I use my own timer to synchronize the images, a technique that Microsoft recommends when using more than one PATH control on the same Web page.

The MoveIEObjects subroutine calls the Tick method for each of the path controls, moving each of the objects to the next coordinate defined for the specific control. It issues another setTimeout function call to continue the animation process, after first testing to see if the last movement of the path has occurred (the last of the ticks, of which there are eight for this particular demonstration). Note that I did not necessarily have to test the tick movement because after the path has completely played, it no longer moves unless it is rewound. However, I want to make sure the timer is not still operating. The complete text of MoveIEObjects is given next:

Next, I add the JavaScript to control the animation for Navigator 4.0. The cycle function for Navigator is similar to that for IE 4.0, except that I need to create my own arrays of X and Y coordinates. These arrays, in turn, are set to hold other arrays holding the actual coordinates. After the arrays are created, the images are displayed and the timer is started, as seen next:

The final script is for the MoveObjects function for Navigator. The LAYER tag method moveTo is used to move each of the images to the new coordinate for this timer event. Note that the timer values are set to be much larger than the ones set for IE 4.0 as the PATH control does run a bit slower than using the LAYER tag approach, and I wanted both effects to be as similar as possible. MoveObjects can be seen next:

Note that resizing Navigator can misalign the images. Netscape recommends a blank document.write statement in JavaScript to compensate for this type of problem, but this does not work for this example. I will continue to investigate this and will post a fix to my Web site when one appears. In the meantime, if you resize the Web page you need to reload the example. No problem occurs when you resize IE.

That’s it for the scripting. You can test the example yourself if you are using either IE 4.0 or Netscape 4 (this script will not work on earlier browsers). View the complete source by using the appropriate “View Source” option of your browser.