JavaScript Editor JavaScript Validator     JavaScript Editor 



Team LiB
Previous Section Next Section

Dynamic HTML in Netscape Navigator 4.x

As we said at the beginning of the chapter, the DHTML supported by Netscape Navigator (NN) 4.x is very, very different from that supported in IE 4.0+ due to the differences between the BOMs for the browsers. There are still some similarities, such as the use of style sheets, and using the ID in HTML tags to identify tags, but the programming is quite different. NN 4.x supports style sheets that allow the attributes of tags to be defined as the page loads, but, generally speaking, its ability to change the page after loading is more limited than it is with IE. Also, where IE makes every tag on a page available to us as a particular object type with its own specific properties, methods, and events (for example, the <div> tag has the Div object, the <P> tag has the P object), NN 4.x makes only some tags available in this way. Most tags are instead represented by just one object type, the Layer object. This object is key to NN 4.x DHTML.

The <layer> Tag and Layer Object

First introduced in NN 4.0 and not supported by Netscape 6 or 7, the <layer> tag, represented in scripting as the Layer object, is the key to dynamically changing content on a page after it has been loaded.

The easiest way to think of layers is as separate windows in the page that have their own document objects that we can use.

Creating Layers

A number of ways are available to create a Layer object. Here we'll concentrate on three of them. The first two ways are explicit creation using the <layer> and <ilayer> tags. The third way is by positioning the element as absolute or relative with style sheets, in which case NN reflects the element into JavaScript as a Layer object whose properties, such as left, top, and visibility, we can change.

The Layer objects in a page can be referenced in the document object's layers array property, by index or id value.

<layer> and <ilayer> Tags

The simplest way to create a Layer object is by using either the <layer> or the <ilayer> tag. The <layer> tag produces an invisible rectangle of screen space in front of other non-layer content in the page. It is positioned absolutely, and can be repositioned, as well as written to, after the page has loaded. The <ilayer> tag creates an inline layer, which is positioned relatively and can be repositioned. However, changing content inside its document is very difficult and usually best avoided.

Both <layer> and <ilayer> tags can contain other tags and help group things together.

We would define a normal <layer> like this:

<layer id="myLayer1" left="100" top="250">
   <h3>Content within the layer</h3>
   <P>a paragraph in a layer</P>
</layer>

For an inline layer, we use a nearly identical definition. The difference is that left is not absolute left, but relative to the previous tag. The same applies to the TOP attribute.

<ilayer id="myILayer1" left="100" top="250">
   <h3>Content within the ilayer</h3>
   <P>a paragraph in an ilayer</P>
</ilayer>

To reference either of the two layers from JavaScript, we use its id via the document object. For example, to move the <layer> tag with id of myLayer1, we would use the following:

document.myLayer1.left = 200;

To change the background color of the <ilayer> tag with id of myILayer1, we would use the following:

document.myILayer1.bgColor = "red";
Implicit Layer Creation

To implicitly create a layer object for any tag, we need to use style sheets to specify whether it should be positioned absolutely or relatively. We saw how to do this at the beginning of the chapter. If we do this, NN will automatically create a Layer object for the tag, although we need to remember to give it an id value to make it easier to reference later. Even without an id, it's possible to access a Layer object in a page via the document object's layers array property, but using id values makes identifying which tag the code is acting on that much easier.

For example, say we defined a <div> tag with absolute positioning, like this:

<div style="position:absolute;left:0px;top:100px;" id="div1">
<P>
   My absolute positioned div containing a paragraph
</P>
</div>

We can access the Layer object implicitly created by using the unique ID.

document.div1.left = 200;

An alternative method of specifying the style is to use the <style> tag to create style classes, and then use a tag's class attribute to set its style to a predefined class, as we saw at the beginning of the chapter.

Although we've used a <div> in the example, it is possible to use many of the HTML tags available. However, while it's possible, it's not generally a good idea to do so due to bugs in Netscape, particularly the earlier versions of NN 4. The <div> and <span> tags work fine as far as positioning goes, but other tags can prove more risky: They might work or we might find all sorts of problems.

Moving Layers

The Layer object has two properties that allow us to reposition a layer after the page has loaded; these are top and left. They take values (in pixels) that reposition the layer.

For example, say we define our <layer> tag like this:

<layer id="myLayer1" left="100" top="250">
   <h3>Content within the layer</h3>
   <P>a paragraph in a layer</P>
</layer>

We could reposition it (absolutely, because the <layer> tag is automatically positioned absolutely) by using the following script:

document.myLayer1.left = 500;
document.myLayer1.top = 100;

Additionally, the Layer object has the moveTo() and moveBy() methods in which we can specify the horizontal and vertical movement of the Layer at the same time. The method moveTo() moves to a point on the screen specified by the parameters, and moveBy() moves the tag along and down by the parameters passed. They work with absolutely and relatively positioned layers, although the moveTo() method will use the coordinate system of the containing tag rather than the page if it is for a relatively positioned layer.

For example, if we want to reposition our layer at 200 pixels across the screen and at 150 pixels down the screen, we could write the following:

myLayer1.moveTo (200,150);

If we want to move our layer by a certain number of pixels, for example if the layer is at 100 along by 200 down and we want to move it along 10 pixels and up 5 pixels, we'd write this code:

myLayer1.moveBy (10,-5);

Note how a negative value moves the layer up the screen.

What would happen if two layers were placed on top of each other? Well, the order of the layers is determined by their z-index attribute. The higher the value of this attribute, the further into the foreground the layer becomes. We can define this when we create the tag.

<layer id="myLayer1" left="100" TOP="250" z-index="1">
   <h3>Content within the layer</h3>
   <P>a paragraph in a layer</P>
</layer>

This layer will be positioned above any layers with a z-index of 0. As it happens, 0 is the default value if we don't specify the z-index attribute, and so this element will be positioned above all elements without a defined z-index attribute.

We can change the z-index attribute value after the page has loaded via script. We can either specify the z-index directly using the zIndex property of the Layer object (note the omission of the hyphen), or we can use the moveAbove() and moveBelow() methods of the Layer object. Both methods take the id of the Layer object we want to move as a parameter.

Try It Out – The z-index

Let's look at this in the simple example that has two partially overlapping <div> tags, each with a different z-index style attribute. Because each <div> is positioned absolutely using style sheets, a Layer object, accessible via the unique ID attribute specified in each <div> tag's HTML, will be created for each <div>.

<html>
<head>
<style>
   div { position: absolute; left : 100px; }
</style>
</head>
<body>
<div id="layer1" style="background-color: red; top: 100;z-index: 2">
HHHHHHHH
<br>
HHHHHHHH
<br>
HHHHHHHH
</div>
<div ID="layer2" style="background-color: blue; top: 120; z-index: 1">
XXXXXXXX
<br>
    
XXXXXXXX
<br>
XXXXXXXX
</div>
<form>
<input type="button" value="Red Div Above" onclick="layer1.moveAbove(layer2)"

   NAME=button1>
< input type="button" value="Red Div Below" onclick="layer1.moveBelow(layer2)"
   name=button2>
</body>
</html>

Save this as zindex_NN.htm, and load it into an NN 4.x browser. We should get a page similar to the one in Figure 12-14. Note that the red <div> (with the H values) is uppermost.

Click To expand
Figure 12-14

If we click the Red Div Below button, the page will change to the look like the one in Figure 12-15.

Click To expand
Figure 12-15

The uppermost <div> is now blue (containing X values). Click the Red Div Above button, and the page will change to the way it was before.

How It Works

In the body of the page we define two <div> tags, each positioned absolutely using a style class. The first <div> has a red background, has a z-index of 2, and is filled with HHHHHHH. The second has a blue background, has a z-index of 1, and is filled with XXXXXXX. Initially, because the blue <div> has a lower z-index number, it is partially hidden by the red <div>. However, the code in the Red Div Below button's onclick event handler, layer1.moveBelow(layer2), causes the red layer, layer1, to move below layer2, which we pass as a parameter to the moveBelow() method. Click the Red Div Above button and this time its onclick event handler contains layer1.moveAbove(layer2), which moves the red <div> above the blue <div>.

Try It Out – Moving Netscape Paragraphs

Let's look at another example. We'll rewrite the MovingTags_IE.htm example we created for IE, so that it will work with NN 4.x.

The code for the NN moving paragraphs is almost identical to the IE version, so load MovingTags_IE.htm into your web page editor and make the four line alterations necessary.

<html>
<head>
<script language=JavaScript>
var paraOneLeft = 100;
var paraTwoLeft = 400;
var switchDirection = false;
function window_onload()
{
   window.setInterval("moveParas()",50);
}
function moveParas()
{
   if (switchDirection == false)
   {
      paraOneLeft++;
      paraTwoLeft--;
      if (paraOneLeft == 400)
      {
         switchDirection = true;
      }
   }
   else
   {
      paraOneLeft--;
      paraTwoLeft++;
      if (paraOneLeft == 100)
      {
         switchDirection = false;
      }
   }
   document.para1.left = paraOneLeft;
   document.para2.left = paraTwoLeft;
}
</script>
</head>
<body language=JavaScript onload="return window_onload()">
<div id="para1" style="position:absolute;left:100;top:30"><P>Para 1</P></div>
<div id="para2" style="position:absolute;left:400;top:10"><P>Para 2</P></div>
</body>
</html>

Save the page as MovingTags_NN.htm.

How It Works

The page is identical to MovingTags_IE.htm apart from four lines. First there are the two lines of HTML defining the paragraphs.

<div id="para1" style="position:absolute;left:100;top:30"><P>Para 1</P></div>
<div id="para2" style="position:absolute;left:400;top:10"><P>Para 2</P></div>

We've moved the paragraphs inside <div> tags, which we have given the same id values and properties that the paragraph tags had in the IE version. The big question is, of course, why?

Well, as mentioned earlier, while we can in theory position most tags with style sheets and then move them around with their Layer object, this can be rather risky due to bugs in the Netscape browser, bugs that vary with operating system and version of Netscape. Sometimes it will work and we'll get away with it, and other times it leads to disaster. We'd probably get away with doing this with the <P> tag, but trying to move tables, table cells, lists, and so on is best avoided. So for safety reasons I've used <div> tags in the examples.

The second two lines that have changed are those that assign the left positions of the paragraphs with the revised values.

document.para1.left = paraOneLeft;
document.para2.left = paraTwoLeft;

With NN we specify the document object, but we don't need to specify the style object as we did in the IE version. For comparison in the IE version, these two lines were as follows:

para1.style.left = paraOneLeft;
para2.style.left = paraTwoLeft;

Although the code is very similar, it hides big differences underneath. With IE we are referencing the style object of the tag itself. With NN we are not actually accessing the tag, but instead the Layer object that was created for the tag and placed in the layers[] array property of the document object.

Changing Content within a Layer

We mentioned previously that it's helpful to think of layers as windows in the page, and this is particularly relevant when changing the content inside a layer. Every Layer object has the document property, which gives access to that layer's document, just as the document property of the window object gives access to the page within a window. We can do many of the things to a Layer's document that we can do to a window's document. For example we can use the document.write() method to rewrite HTML to the Layer's document.

For example, we can define a <div> tag like this:

<div id=mydiv style="position:absolute;left:10;top:10"
<h2>
   A Heading
</h2>
</div>

Then, to change the content inside the <div> and remove the <h2> tag, we first open the <div> tag's Layer object's document.

document.myDiv.document.open();

Then we just use document.write() method.

document.myDiv.document.write("<P>Now it's a paragraph</P>");

We close the document after we've finished adding content, otherwise any later document.write()s will add to and not replace the HTML inside the Layer.

document.myDiv.document.close();

We're not restricted as to what HTML we insert, as long as it's valid and we make sure the appropriate close tags are used.

Layer Events

Unfortunately, although IE provides every tag with many new events, under NN 4.x we're generally left with the standard HTML events made available in version 3 browsers. The Layer object has events including onmouseover, onmouseout, onmouseup, onload, onfocus, and onblur.

With a Layer object created by a <layer> tag or an <ilayer> tag, we can use our standard method of attaching code to the event handler as an attribute of the tag. Note that with <ilayer> you can write event handler attributes, but they are ignored. Only <layer> takes event handler attributes.

For example, if we have a Layer object defined using the <layer> tag and we want to connect the function myLayer_onmouseover() to the onmouseover event handler, we just add the onmouseover attribute to the tag's definition.

<layer id="myLayer" left=10 top=10 onmouseover="myLayer_onmouseOver()">
   Layer html
</layer>

However, if it's an implicitly created Layer object, as with absolutely or relatively positioned tags, we can't simply add the event name to the tag's HTML definition. For example, if we have a <DIV> which, due to being positioned absolutely, has a Layer object defined for it and we want to connect that Layer object's onmouseover event handler to the function myDiv_onmouseover(), we need to write the following:

<div id="myDiv" style="position:absolute;left:10;top:10">
   <P>A paragraph inside the div</P>
</div>
<script language="JavaScript">
   document.myDiv.onmouseover = myDiv_onmouseover;
</script>

We need to have defined the function myDiv_onmouseover() earlier in the page, prior to trying to connect it to the onmouseover event handler.

Try It Out – Roll Over Heading

In the earlier section on IE DHTML, we had an example in which the color of a paragraph changed when the user's mouse pointer rolled over it. The color reverted to the original color when the mouse rolled off of it. Let's try the same in NN.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<body>
<div id=RollOverDiv style="position:absolute;left:10px;top:10px">
   <P style='color:black'>Roll your mouse over me</P>
</div>
<script language=JavaScript type="text/javascript">
function RollOverDiv_mouseOver()
{
   document.RollOverDiv.document.open();
   document.RollOverDiv.document.write("<P STYLE='color:red'>" +
      "Wow that feels good</P>");
   document.RollOverDiv.document.close();
}
function RollOverDiv_mouseOut()
{
   document.RollOverDiv.document.open();
   document.RollOverDiv.document.write("<P STYLE='color:black'>" +
      "Roll your mouse over me</P>");
   document.RollOverDiv.document.close();
}
document.RollOverDiv.onmouseover = RollOverDiv_mouseOver
document.RollOverDiv.onmouseout = RollOverDiv_mouseOut
</script>
</body>
</html>

Save the page as RollOverHeading_NN.htm and load it in your browser. When the mouse pointer rolls over the text, the text changes and turns red.

How It Works

At the top of the page we define our <div> tag.

<div id=RollOverDiv style="position:absolute;left:10;top:10">
   <P style='color:black'>Roll your mouse over me</P>
</div>

Because it has been positioned absolutely through its style attribute, a Layer object will be created for it, which we can access to make the rollover changes.

In the following script block we define two functions that will be connected to the onmouseover and onmouseout events of the <div>.

function RollOverDiv_mouseOver()
{
   document.RollOverDiv.document.open();
   document.RollOverDiv.document.write("<P style='color:red'>" +
      "Wow that feels good</P>");
   document.RollOverDiv.document.close();
}
function RollOverDiv_mouseOut()
{
   document.RollOverDiv.document.open();
   document.RollOverDiv.document.write("<P style='color:black'>" +
      "Roll your mouse over me</P>");
   document.RollOverDiv.document.close();
}

In both functions we access the <div> tag's Layer object's document using the following line:

document.RollOverDiv.document

We substitute new HTML inside the layer's document using document.write(). When the mouse pointer rolls over the <div>, we write a different paragraph and make its color red. When it rolls out of the heading, we write the original text and color into the document. In both cases we make sure that we close the document using the document.close() method. If we don't do this we'll find that instead of replacing the HTML in the document the next time we roll over or roll out of the header with our mouse pointer, we'll keep adding to it instead.

The final thing to do in the script block is to connect the onmouseover and onmouseout events of the Layer object for the paragraph tag to our functions.

document.RollOverDiv.onmouseover = RollOverDiv_mouseOver
document.RollOverDiv.onmouseout = RollOverDiv_mouseOut

The NN event Object

NN, like IE, also supports an event object, which, like the IE version, contains properties that help us find out more about the event that fired. However, the name and purpose is almost all that the two objects have in common.

An event object is created automatically for us whenever an event occurs. Unlike in IE, it's not available globally, but instead only in the event handler's code. This means that if we want to access the event object, for example from a function called by an event handler, we need to pass the event object to the function as a parameter when we define our event handler.

For example, following we have an <A> tag with a onclick event handler connected to a function, mylink_onclick().

<script language=JavaScript>
function mylink_onclick ()
{
// Event Code
}
</script>
<A href="#" id=myLink onclick="myLink_onclick()">My Link</A>

To make use of the event object, we need to change the definition of the <A> tag so that the event object, which is available only in the event connection code, is passed to the mylink_onclick() function.

<script language=JavaScript>
function mylink_onclick(theEventObject)
{
   alert(theEventObject.type);
}
</script>
<A href="#" id=myLink onclick="mylink_onclick(event)">My Link</A>

In the preceding code, the alert box will display the event object's type property, which is the type of event that was fired, such as click, mouseout, mouseover, and so on.

Alternatively, we can connect an event handler directly to a function as a property rather than an attribute.

document.onclick = myFunction;

Then we can define a parameter in the definition of the event handling function, myFuntion(), that will automatically be set to the relevant event object by JavaScript.

function  myFunction(eventObject)
{
   alert(eventObject.type);
}

The parameter eventObject will automatically reference the event object without us doing a thing.

Try It Out – The NN event Object

The following example demonstrates some of the event object's properties.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<script language=JavaScript type="text/javascript">
function document_onclick(evtObj)
{
   if (evtObj.which == 1)
   {
      document.xLayer.left = evtObj.x;
      document.xLayer.top = evtObj.y;
   }
}
document.onclick = document_onclick;
</script>
</head>
<body>
<layer id="xLayer" top=-100 left=0>
   <h3>X</h3>
</layer>
</body>
</html>

Save this code as EventObject_NN.htm, and load into your NN 4.x browser. When the document in the browser is left-clicked, an X is placed at that point in the page.

While this is not that useful as it is, we could alter the example so that instead of an X, a popup menu appears with a list of options from which the user can choose.

How It Works

First we define the <layer> tag that will hold the X. We could have defined a <div> tag and used the layer object created automatically; this just demonstrates a variation. It's this layer that will be moved to the position of the latest mouse click, giving the effect that an X has been placed on the spot the user just clicked. By setting the TOP attribute of the <layer> to -100 we can be sure that the layer will start off-screen. We want the users to see the X only on the first time they click the page.

<layer id="xLayer" top=-100 left=0>
   <h3>X</h3>
</layer>

The code works by connecting the document object's onclick event handler to our function document__onclick().

document.onclick = document_onclick;

We've connected to the event in this way because there is no tag defining the document that we can add the onclick event handler to as an attribute.

The function itself makes use of the event object, which is automatically passed as the first parameter when we connect as we have previously.

function document_onclick(evtObj)
{
   if (evtObj.which == 1)
   {
      document.xLayer.left = evtObj.x;
      document.xLayer.top = evtObj.y;
   }
}

The first property of the event object we use is the property in the if statement. The property contains either 1 for the left mouse button, 2 for the middle button, or 3 for the right button. If a key was pressed, the property contains the ASCII value of the key pressed. Note that some ASCII characters are unavailable because they are hotkeys, like F1, and Ctrl+A.

If the left mouse button was pressed, we move the layer, xLayer, to the current mouse position, which is contained in the event object's x and y properties.

That completes the brief tour of DHTML under NN. Our final task is to create the same dynamic menus that we created under IE.

Dynamic Menus in NN 4.x

For the NN 4.x version of the DHTML menus, we'll be using many of the same principles we used in the IE 4.0+ version. First we'll be creating the menus themselves using information in an array that is virtually identical to the IE array, except that we've added an extra bit of information. We'll also be using the same idea of hiding the menus off-screen until the user's mouse rolls over the image. The key difference between the versions of the code is that we're using layers for our menus rather than tables and <div> tags. The NN version is shown in Figures 12-16 and 12-17.

Just like with the IE version, when the user rolls the mouse pointer over the menu images another submenu pops out with options from which to choose. Again as the mouse pointer rolls over a menu item, it changes the background and text color to give a visual indication of the menu item currently selected.

As with the IE version we'll first look at the principles behind the menu system; then we will look at the HTML, style sheets, and code necessary to create a simplified version of the menu system. Then we'll make changes to this simpler version so that all the dynamic HTML is written into the page using document.write(), with the menu generating function gaining its information from arrays, as with the IE version. Finally, we'll wrap up by explaining the new code.

Click To expand
Figure 12-16
Click To expand
Figure 12-17

THE NN DHTML Menu System Principles

The principles behind creating the IE menu system are almost identical to those used to create the Netscape version. The main changes are the tags used and the code used to manipulate those tags. You'll find the <div> tags of the IE version have mostly been replaced by <layer> tags.

Let's briefly run down the high-level principles of the Netscape system. Once we've done that we'll take a detailed look at all the tags and code necessary.

As with the IE version we have an <img> tag: When the user runs a mouse pointer over it, a menu appears just to the right of the image. With the IE version, the menu was contained in a <div> tag, which contained the various menu items. With the NN version the menu items are now stored in a <layer> tag. Note that in Netscape 4 the <img> tag doesn't have the mouseover or mouseout events, so that's why we use the layer's mouseover and mouseout events. As with the IE version, the <layer> tag for the menu starts life off-screen. When we want to display it, we simply move it onto the screen at the correct position. When the mouse pointer leaves the image or the menu layer, we hide the menu <layer> off-screen.

With IE we found that the <div> tag's onmouseout event handler was called not just if the mouse pointer left the <div> but also if it went inside the <div> and over elements in there. With the <layer> tag this does not happen: The onmouseout event handler fires only when the mouse pointer leaves the <layer> altogether, not when it goes inside the <layer>. This makes it easier for our coding.

In the IE version each menu item was contained inside a table row and table cell inside the menu <div>. When the user rolled the mouse pointer from one row to another, we changed the style object of the table cell to indicate which menu the user selected. In the NN version, we still give the appearance of highlighting a menu item when the mouse pointer rolls over it, but the HTML and code is very different. In the NN version each menu item is contained inside its own <layer> tag. Initially each menu item is simply plain text on an orange background. When the user's mouse pointer rolls over the tag, we write HTML to the menu item layer's document. The HTML is simply a hyperlink, but the style sheets provide the change of appearance by making the hyperlink text white and removing the underlining. We also change the layer object's background to blue. If the hyperlink is clicked, the user is taken to the page specific to that menu item and specified in the <A> tag's href attribute. When the mouse pointer rolls off the menu item Layer, we rewrite the plain text to its document.

DHTML Menus Simple Version

As with the IE version, in this simple NN version we'll create only the Wood menu; we'll add the Metal and Bricks menus when we build the final version. We'll create all the code and HTML except the parts that build up our page with document.write() and arrays.

The DHTML

First, we have the image over which the user rolls the mouse pointer to display the menu.

<layer id=WoodMenuImgLayer
   onmouseover="showNNMenu(event)"
   onmouseout="hideNNMenu(event)"
   left=10
   top=10>
      <img src="WoodButton.gif" border=0>
</layer>

The image is inside a <layer> tag with a unique id, which is positioned absolutely using the <layer> tag's left and top attributes. We use the associated Layer object to capture the onmouseover and onmouseout events and also to calculate where the menu layers need to be positioned. We've set the onmouseover event handler to call showNNMenu() and pass the event object as a parameter. The event handler onmouseout calls the function hideNNMenu() and again passes the event object.

Each menu is also contained in a <layer> tag.

<layer id="WoodMenuLayer" left=120 top=-1400 z-index=99
   onmouseout="this.top = -1400;">
</layer>

We've given the tag a unique id and used the <layer> tag's left and top attributes to set it off-screen. The z-index attribute is set to 99 to make sure it appears in front of any other layers on the screen. (A word of warning: Although the menu will appear in front of most content, the way Netscape handles forms means that the menu layer will appear behind a form and there's little we can do about this.) Finally, we've added an event handler for the onmouseout event that causes the menu to hide itself by setting the top style attribute to -1400—off-screen—if the mouse pointer moves off the menu. This differs from the IE version where we called the hideMenu function in this event handler's code.

So far what we have is an empty menu layer, so let's add the first menu item, Oak Timber.

<layer id="WoodMenuLayer" left=120 top=-1400 z-index=99
   onmouseout="this.top = -1400;">
      <layer id="WoodOak" top=0 left=0
         class="LayerItemMenu"
         width=175 height=20
         bgcolor="orange"
         onmouseover="showOverMenu(this,'Oak Timber','OakWood.htm')"
         onmouseout="showOutMenu(this,'Oak Timber')">
            Oak Timber
      </layer>
</layer>

Each menu item is represented by its own <layer> tag. We've given each <layer> tag a unique id, in the code above WoodOak, and set its top and left attributes to zero so that the menu item appears at the top left of the inside of the <layer> tag with id WoodMenuLayer. We've applied the style class LayerOutMenu that gives the menu its appearance. We'll see the style defined later. The <layer> tag has the BGCOLOR attribute, which here we've defined as orange. Style sheets can be used to define the background, but in Netscape using style sheets tends to mean that the background is given a border, letting the page beneath show through.

When the user's mouse pointer rolls over the menu item, we want to change the menu item layer's style. When it rolls out again, we want to change the style back. To do this we've added the onmouseover and onmouseout event handlers, which call the showOverMenu() and showOutMenu() functions, respectively. We'll create and look at these functions later.

All the other menu items follow similar patterns, the differences being the unique ID for the layer, the TOP attribute that specifies the position of the <layer> within the outer menu <layer>, and the values passed to the showOverMenu() and showOutMenu() functions. Let's add all the other menu items.

<layer id="WoodMenuLayer" left=120 top=-1400 z-index=99
   onmouseout="this.top = -1400;">
      <layer id="WoodOak" TOP=0 LEFT=0
         class="LayerItemMenu"
         width=175 height=20
         bgcolor="orange"
         onmouseover="showOverMenu(this,'Oak Timber','OakWood.htm')"
         onmouseout="showOutMenu(this,'Oak Timber')">
            Oak Timber
      </layer>
      <layer id="WoodTeak" top=22 left=0
         class="LayerItemMenu"
         width=175 height=20
         bgcolor="orange"
         onmouseover="showOverMenu(this,'Teak Timber','TeakWood.htm')"
         onmouseout="showOutMenu(this,'Teak Timber')">Teak Timber
      </layer>
      <layer id="WoodPine" top=44 left=0
         class="LayerItemMenu"
         width=175 height=20
         bgcolor="orange"
         onmouseover="showOverMenu(this,'Pine Timber','PineWood.htm')"
         onmouseout="showOutMenu(this,'Pine Timber')">Pine Timber
      </layer>
      <layer id="WoodYew" top=66 left=0
         class="LayerItemMenu"
         width=175 height=20
         bgcolor="orange"
         onmouseover="showOverMenu(this,'Yew Timber','YewWood.htm')"
         onmouseout="showOutMenu(this,'Yew Timber')">Yew Timber
      </layer>
</layer>
The Style Sheets

Our HTML makes use of a number of style classes, so let's look at them in turn.

First we want to avoid having any borders around our images or layers, because they tend to mess up the appearance and cause problems with onmouseover and onmouseout events. For example, if there's a border, to the user it looks like the mouse pointer has moved outside the image, and the onmouseout event does not fire because the mouse pointer is on the invisible border. Note that Netscape adds its own 3-pixel border around all elements anyway. So, the first thing we do in our style sheet is limit borders by specifying a style that will apply to all <layer> and <img> tags.

<style>
   layer { border : none 0px;}
   img { border : none 0px;}

Next we need to specify the appearance of menu items.

   .LayerItemMenu
   {
      font-family:verdana;
      font-size:60%;
      margin:0;
      padding:0;
      border: 3px groove lightgrey;
   }

We've seen this class used for the menu item <layer> tags previously. As well as specifying font-family and size, we've also tried to limit any possible padding or margins around our elements because we want the menu items to sit as close as possible to each other. Finally, we specify a 3-pixel wide grooved border in light gray. (Note, for some odd reason, the British English spelling of gray with an e. This happens only with lightgrey and not with gray!)

The final style class we need is for the links inside the menu items when they are rolled over by the mouse pointer. We'll see this in use when we see the showOverMenu() function.

   .textLink { color: white; text-decoration: none;}
</style>
Showing and Hiding the Menus

Let's start with the function that displays the menu when the mouse pointer rolls over a menu image, the showNNMenu() function.

function showNNMenu(evt)
{
   var srcLayer = evt.target;
   var menuLayerId = srcLayer.name. replace("ImgLayer","Layer");
   document.layers[menuLayerId].top = srcLayer.pageY;
   document.layers[menuLayerId].left = srcLayer.pageX +
      srcLayer.clip.width + 4;
}

The function has just one parameter, evt, which is the event object that we pass in the onmouseover event handler connected to the <layer> around the image. Remember the event object is something the browser creates for us automatically and populates with various useful pieces of information about the event that fired.

The first line of the function uses the event object's target property, which passes us a reference to the object element that is at the center of the event, in this case the Layer object of the <layer> tag we put our <img> inside.

We know which layer was rolled over, but we don't know which menu layer we need to display. However, we can find out easily because of the naming convention we've used. All <layer> tags around menu images are named WoodMenuImgLayer, MetalMenuImgLayer, or BrickMenuImgLayer. They all start with the name of the menu to be displayed and all end with the words MenuImgLayer. All our menu item <layer> tags also follow a similar convention being named WoodMenuLayer, MetalMenuLayer, and BricksMenuLayer. Again they start with the name of the menu and end with MenuLayer. Given this information, we know that if WoodMenuImgLayer has been rolled over, we need to display the WoodMenuLayer layer. How can we deal with this in code? Easy. We just replace the ImgLayer part with Layer and, hey, presto, we have the unique id of the menu layer we need to display. This is exactly what the second line of the function does.

Now we determine which layer to move onto the screen. We do so on the third and fourth lines. We use document.layers[] to access the layers array property of the document object, which gives us a reference to the Layer object whose ID we pass inside the square brackets. In our case, it's the ID inside variable menuLayerId, which is the ID of the Layer holding the menu items. First we set the top property of this Layer object to the same level as the srcLayer, that is, the layer around the image that was rolled over. Then in the final line we set the left property of the menu Layer object to be the pageX property of the Layer object around the image, plus the width of the layer. We add 4, just to make sure the layer around the image and the menu layer don't overlap; otherwise the onmouseout and onmouseover events won't fire correctly.

We've seen how to show the menu layer, so now let's look at the function that hides it when the mouse pointer leaves the image. This is connected to the onmouseout event handler of the layer around the image.

function hideNNMenu(evt)
{
   var srcLayer = evt.target;
   if (evt.pageX < srcLayer.clip.right)
   {
      var menuLayerId = new String(srcLayer.name).replace("ImgLayer","Layer");
      document.layers[menuLayerId].top = -1400;
   }
}

If the user moves the mouse pointer off the layer around the image, above, below, or to the left of it, we want to hide the menu again. However, if the mouse pointer moves to the right off the image, it will pass over to the menu's layer, so we do not want to hide it.

The event object, passed as a parameter evt to the function, contains all the information we need to find out where the mouse pointer has moved. Again we declare and set the variable srcLayer to the target of the event, namely the Layer object of the <layer> tag around the <img> tag. Then if evt.pageX, the current horizontal position of the mouse pointer on the page, is less than the right edge of the layer around the image, we hide the menu by moving it off-screen. The right edge of the image is available through the clip.right property of the Layer object.

As with the showNNMenu() function, we find out which menu layer we need to hide from the id of the Layer around the image. Using that, we replace the ImgLayer part with Layer and we have the ID that we can use to get a reference to the Layer to hide, using the document.layers array. We can then set this menu Layer off-screen by setting its top property to an appropriate value.

Menu Rollover and Rollout Style Changes

Let's look at the showOverMenu() function, the function that changes the style of a menu item when we roll over it. This function is connected to the onmouseover event handler for each <layer> containing a menu item.

function showOverMenu(menuLayer, menuText, menuPage)
{
   menuLayer.bgColor = "blue";
   menuLayer.document.open();
   menuLayer.document.write('<div class="LayerItemMenu">');
   menuLayer.document.write('<A href="' + menuPage + '" class="textLink">'

      + menuText + '</A>');
   menuLayer.document.write('</div>')
   menuLayer.document.close();
}

The function has three parameters. The first is the Layer object for the menu item layer we want to change. The second parameter is the text of the menu we want to display, such as Oak Timber, and the third is the source for the hyperlink that takes the user to the page for that menu item, for example the OakWood.htm page for the Oak Timber menu item.

In the first line we use the Layer object referenced in menuLayer to set the background color to blue. Then we open the layer's document for writing and write a <div>, inside which is a hyperlink (<A>) that if clicked will take the user to the page relevant for that menu item. Note we're using the style classes defined earlier in the style sheet for the <div> and <A> tags. In addition to applying style to the tags, the class for the <A> tag also makes sure that the link has white text and is not underlined like a link would normally be.

Finally we close the document.write(). Next time we use document.write() it will clear the current contents.

The showOutMenu() function works in a similar way to showOverMenu() in that it uses document.write() to change the contents of the HTML inside the menu item layer to return it to the look it had before the mouse rolled over it. The showOutMenu() function is called by the onmouseout event handler for each <layer> containing a menu item.

function showOutMenu(menuLayer, menuText)
{
   menuLayer.bgColor = "orange";
   menuLayer.document.open();
   menuLayer.document.write('<div class="LayerItemMenu">' +
      menuText + '</div>');
   menuLayer.document.close();
}

This time there are only two parameters: the Layer object of the menu item that needs to be acted upon, and the text to be displayed. Because no hyperlink will be created (the users can't click a link if they are not over it), there is no need for the final hyperlink source parameter of the previous function.

In the first line, we set the background color of the menu item to orange. Then, we start the document writing with an open() method and just write our <div> and the text to be displayed inside it. Finally, we close the layer's document.

Putting It Together: A Simple DHTML Menu Example

Well, that's all the HTML and code we need for our simple version, so let's create that in a full page.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<style>
   layer { border : none 0px;}
   img { border : none 0px;}
   .LayerItemMenu {
      font-family:verdana;
      font-size:60%;
      margin:0;
      padding:0;
      border: 3px groove lightgrey;
   }
   .textLink { color: white; text-decoration: none;}
</style>
<script type="text/javascript" language="JavaScript">
function showNNMenu(evt)
{
   var srcLayer = evt.target;
   var menuLayerId = new String(srcLayer.name).replace("ImgLayer","Layer");
   document.layers[menuLayerId].top = srcLayer.pageY + 10;
   document.layers[menuLayerId].left = parseInt(srcLayer.pageX
      + srcLayer.clip.width + 4);
}
function hideNNMenu(evt)
{
   var srcLayer = evt.target;
   if (evt.pageX < srcLayer.clip.right)
   {
      var menuLayerId = new String(srcLayer.name).replace("ImgLayer","Layer");
      document.layers[menuLayerId].top = -1400;
   }
}
function showOverMenu(menuLayer, menuText, menuPage)
{
   menuLayer.bgColor = "blue";
   menuLayer.document.open();
   menuLayer.document.write('<div class="LayerItemMenu">');
   menuLayer.document.write('<A href="' + menuPage
      + '" class="textLink">' + menuText + '</A>');
   menuLayer.document.write('</div>')
   menuLayer.document.close();
}
function showOutMenu(menuLayer, menuText)
{
   menuLayer.bgColor = "orange";
   menuLayer.document.open();
   menuLayer.document.write('<div class="LayerItemMenu">'
      + menuText + '</div>');
   menuLayer.document.close();
}
</script>
</head>
<body>
<layer id=WoodMenuImgLayer
   onmouseover="showNNMenu(event)"
   onmouseout="hideNNMenu(event)"
   left=10
   top=10>
      <img src="WoodButton.gif" border=0>
</layer>
<layer id="WoodMenuLayer" left=120 top=-1400 zindex=99
   onmouseout="this.top = -1400;">
      <layer id="WoodOak" top=0 left=0
         class="LayerItemMenu"
         width=175 height=20
         bgcolor="orange"
         onmouseover="showOverMenu(this,'Oak Timber','OakWood.htm')"
         onmouseout="showOutMenu(this,'Oak Timber')">
            Oak Timber
      </layer>
      <layer id="WoodTeak" top=22 left=0
         class="LayerItemMenu"
         width=175 height=20
         bgcolor="orange"
         onmouseover="showOverMenu(this,'Teak Timber','TeakWood.htm')"
         onmouseout="showOutMenu(this,'Teak Timber')">Teak Timber
      </layer>
      <layer id="WoodPine" top=44 left=0
         class="LayerItemMenu"
         width=175 height=20
         bgcolor="orange"
         onmouseover="showOverMenu(this,'Pine Timber','PineWood.htm')"
         onmouseout="showOutMenu(this,'Pine Timber')">Pine Timber
      </layer>
      <layer id="WoodYew" top=66 left=0
         class="LayerItemMenu"
         width=175 height=20
         bgcolor="orange"
         onmouseover="showOverMenu(this,'Yew Timber','YewWood.htm')"
         onmouseout="showOutMenu(this,'Yew Timber')">Yew Timber
      </layer>
</layer>
</body>
</html>

Save the page as NNMenus.htm and then load it into Netscape 4.x browser. The menus look and work in a very similar way to the IE ones, though perhaps a little less effectively as they have a tendency to close before you get a chance to roll over them. If this was for a real-world situation, we might consider adding a timer so that the menus stay visible for a second before closing.

The Full NN DHTML Menu Example

We now take a look at the full code for this example. The code makes use of arrays to store the menu information and creates the HTML for the menus dynamically from the array information.

The first change is to the script block in the head of the page where we define the arrays and the function that will be used to dynamically create the HTML for the menus.

<script type="text/javascript" language="JavaScript">
var woodMenuItems = new Array();
woodMenuItems[0] = new Array();
woodMenuItems[0][0] = "Oak";
woodMenuItems[0][1] = "OakWood.htm";
woodMenuItems[0][2] = "Oak Timber";
woodMenuItems[0][3] = 0;
woodMenuItems[1] = new Array();
woodMenuItems[1][0] = "Teak";
woodMenuItems[1][1] = "TeakWood.htm";
woodMenuItems[1][2] = "Teak Timber";
woodMenuItems[1][3] = 22;
woodMenuItems[2] = new Array();
woodMenuItems[2][0] = "Pine";
woodMenuItems[2][1] = "PineWood.htm";
woodMenuItems[2][2] = "Pine Timber";
woodMenuItems[2][3] = 44;
woodMenuItems[3] = new Array();
woodMenuItems[3][0] = "Yew";
woodMenuItems[3][1] = "YewWood.htm";
woodMenuItems[3][2] = "Yew Timber";
woodMenuItems[3][3] = 66;
var metalMenuItems = new Array();
metalMenuItems[0] = new Array();
metalMenuItems[0][0] = "Steel";
metalMenuItems[0][1] = "SteelMetal.htm";
metalMenuItems[0][2] = "Steel Girders";
metalMenuItems[0][3] = 0;
metalMenuItems[1] = new Array();
metalMenuItems[1][0] = "Copper";
metalMenuItems[1][1] = "CopperMetal.htm";
metalMenuItems[1][2] = "Copper Pipes";
metalMenuItems[1][3] = 22;
metalMenuItems[2] = new Array();
metalMenuItems[2][0] = "Gold";
metalMenuItems[2][1] = "GoldMetal.htm";
metalMenuItems[2][2] = "Gold Ingots";
metalMenuItems[2][3] = 44;
var bricksMenuItems = new Array();
bricksMenuItems[0] = new Array();
bricksMenuItems[0][0] = "StdHouse";
bricksMenuItems[0][1] = "StdHousebricks.htm";
bricksMenuItems[0][2] = "Standard House Brick";
bricksMenuItems[0][3] = 0;
bricksMenuItems[1] = new Array();
bricksMenuItems[1][0] = "LargeHouseBrick";
bricksMenuItems[1][1] = "LargeHousebricks.htm";
bricksMenuItems[1][2] = "Large House Bricks";
bricksMenuItems[1][3] = 22;
bricksMenuItems[2] = new Array();
bricksMenuItems[2][0] = "BreezeBlock";
bricksMenuItems[2][1] = "BreezeBlock.htm";
bricksMenuItems[2][2] = "Breeze Block";
bricksMenuItems[2][3] = 44;
function createMenu(menuName, menuItems)
{
   var outerLayerhtml = '<layer id="' + menuName + 'MenuLayer" '
   outerLayerhtml = outerLayerhtml +
      ' onmouseout="this.top = -1400;" left=120 top=-1400 z-index=99>'
   var totalLayers = menuItems.length;
   var layerCount;
   var innerLayerhtml = "";
   var layerName = "";
   for (layerCount = 0; layerCount < totalLayers; layerCount++)
   {
      layerName = menuName + menuItems[layerCount][0];
      innerLayerhtml = innerLayerhtml + '<layer id="' + layerName + '" '
      innerLayerhtml = innerLayerhtml + ' top=' + menuItems[layerCount][3]
         + ' left=0 '
      innerLayerhtml = innerLayerhtml + ' class="LayerItemMenu" '
      innerLayerhtml = innerLayerhtml + ' width=175 height=20 bgcolor="orange" '

      innerLayerhtml = innerLayerhtml + ' onmouseover="showOverMenu(this,\''
         + menuItems[layerCount][2] + '\''
      innerLayerhtml = innerLayerhtml + ',\''
         + menuItems[layerCount][1] + '\')" '
      innerLayerhtml = innerLayerhtml + ' onmouseout="showOutMenu(this,\''
         + menuItems[layerCount][2] + '\')">'
      innerLayerhtml = innerLayerhtml + menuItems[layerCount][2] + '</layer>';
   }
   return outerLayerhtml + innerLayerhtml + '</layer>';
}
function showNNMenu(evt)
{

The next change is within the body of the page. We add the layers containing the images for the other two menus, and instead of the layers for the menus themselves, we insert a script block that calls the createMenu() function we defined previously.

<body>
<layer id=WoodMenuImgLayer
   onmouseover="showNNMenu(event)"
   onmouseout="hideNNMenu(event)"
   left=10
   top=10>
      <img src="WoodButton.gif" border=0>
</layer>
<layer id=MetalMenuImgLayer
   onmouseover="showNNMenu(event)"
   onmouseout="hideNNMenu(event)"
   top=50
   left=10>
   <img src="MetalButton.gif" border=0>
</layer>
<layer id=BricksMenuImgLayer
   onmouseover="showNNMenu(event)"
   onmouseout="hideNNMenu(event)"
   top=90
   left=10>
      <img src="BricksButton.gif" BORDER=0>
</layer>
<script type="text/javascript" language="JavaScript">
   document.write(createMenu("Wood",woodMenuItems));
   document.write(createMenu("Metal",metalMenuItems));
   document.write(createMenu("Bricks",bricksMenuItems));
</script>
</body>

Resave this page as NNMenus.htm. You will also need the three images that were used in the IEMenus.htm example.

Load the page into your NN 4.x browser to test it.

How It Works

As with the IE version, the information for creating the menus is in arrays. The array structure and use is identical to the IE version, except that there is an additional element at the index 3 of the second dimension of the array. This gives the top value for each menu item layer within the enclosing menu layer.

So, each array element contains the following information:

  • menuItems[X][0]—name of the menu item

  • menuItems[X][1]—page to go to if menu item is clicked

  • menuItems[X][2]—text to display for menu item

  • menuItems[X][3]—top position of menu item inside outer menu layer

The menus themselves are created in a very similar fashion to the IE version, using the createMenu() function. The function's purpose is to re-create, based on the menu arrays, the HTML that we discussed in the DHTML principles behind the menus.

Let's have a look at the createMenu() function. We start by actually defining the function. We can see the function takes two parameters, menuName and menuItems. The parameter menuName will be used to generate the unique id values for our <layer> tags. The menuItems parameter is actually a multi-dimensional array. The first dimension is for each menu item and the second, as detailed previously, holds the details for each menu item, such as name, page to go to, text to display, and position of the top of the menu item's <layer> inside the main menu <layer>.

function createMenu(menuName, menuItems)
{

We then build up the start tag of the outer <layer> tag for the menu in the outerLayerhtml variable using the menuName parameter passed to the function to create the ID.

   var outerLayerhtml = '<layer id="' + menuName + 'MenuLayer" '
   outerLayerhtml = outerLayerhtml +
      ' onmouseout="this.top = -1400;" left=120 top=-1400 z-index=99>'

Next we declare and initialize a number of variables that we'll need to create the <layer> tags for each menu item.

   var totalLayers = menuItems.length;
   var layerCount;
   var innerLayerhtml = "";
   var layerName = "";

The variable totalLayers holds the length of menuItems, our array. As the name suggests, this tells us how many menu items need creating. The variable layerCount will be used to count through each array item, and innerLayerhtml will be used to store all the inner <layer> tags inside our main menu <layer>. Finally, the variable layerName will hold a unique id for each menu item layer.

Our next task is to loop through each of the items in the menu array and build up the <layer> tags for each menu item. We can find out how many items there are in the menu from the totalLayers variable defined in the preceding code, and then loop that number of times through the array.

   for (layerCount = 0; layerCount < totalLayers; layerCount++)
   {

The next few lines are fairly straightforward; we are simply concatenating strings to create the HTML for a menu item layer's HTML.

      layerName = menuName + menuItems[layerCount][0];
      innerLayerhtml = innerLayerhtml + '<layer id="' + layerName + '" '
      innerLayerhtml = innerLayerhtml + ' top=' + menuItems[layerCount][3]
         + ' left=0 '
      innerLayerhtml = innerLayerhtml + ' class="LayerItemMenu" '
      innerLayerhtml = innerLayerhtml + ' width=175 height=20 bgcolor="orange" '

The remaining lines of the for loop do look a little confusing at first.

      innerLayerhtml = innerLayerhtml + ' onmouseover="showOverMenu(this,\''
         + menuItems[layerCount][2] + '\''
      innerLayerhtml = innerLayerhtml + ',\''
         + menuItems[layerCount][1] + '\')" '
      innerLayerhtml = innerLayerhtml + ' onmouseout="showOutMenu(this,\''
         + menuItems[layerCount][2] + '\')">'
      innerLayerhtml = innerLayerhtml + menuItems[layerCount][2] + '</layer>';
   }

The reason for the backward slashes is that we are indicating escape characters, in this case the single quote. We need to put them as escape characters, otherwise JavaScript is going to think we mean the end of the string rather than what we really mean, which is a single quote inside a string.

Once one item layer has been created, we loop around again and create the next one, and the next one, and so on until we come to the end of our array's elements.

In our final lines of code in the function, we join the outerLayerhtml containing the main menu <layer> tag, the innerLayerhtml containing the <layer> tags for all the menu items, and add the closing </layer> tag for the outer layer.

   return outerLayerhtml + innerLayerhtml + '</layer>';
}

All this is returned to the code that called the function, this code being given in the body of the page.

<script language=JavaScript>
   document.write(createMenu("Wood",woodMenuItems));
   document.write(createMenu("Metal",metalMenuItems));
   document.write(createMenu("Bricks",bricksMenuItems));
</script>

Each line calls the createMenu() function and writes the HTML returned into the page using document.write().

That completes our look at the NN 4.x version of dynamic menus.


Team LiB
Previous Section Next Section


JavaScript Editor JavaScript Validator     JavaScript Editor


©