JavaScript Editor JavaScript Validator     JavaScript Editor 



Team LiB
Previous Section Next Section

Dynamic HTML in Internet Explorer 4.0+

Under IE, most tags, even simple tags like the <P> tag, are represented by objects. The attributes of these tags are often represented as properties of the objects. The objects also have an amazing number of other properties, as well as methods and events, probably totaling almost a hundred. Some of these properties are themselves objects with even more properties and methods.

In this section, we'll be looking at just the fundamentals of DHTML with IE. With the fundamentals under your belt you can explore more of the properties, methods, and events IE makes available.

Accessing Page Tags

How do we access the objects of HTML tags to change their properties, call their methods, and hook into their events? We obviously need some way of specifying which individual tag we are referring to. So far in this book we have been giving tags a name attribute and using this to access them. For example, a text box within a form could be defined like this:

<form name="myForm">
   <input type="text" name="text1">
</form>

We could then refer to the corresponding Form object in JavaScript using the form's name, myForm, and the text box's Text object using the name, text1.

document.myForm.text1.value

In IE's DHTML, however, we use a different way of uniquely identifying tags, namely with a tag's id attribute. This works in a very similar manner to the way the name attribute works. Certain elements, such as controls on forms, still require the name attribute, because it is passed to a server if the form is posted. However, for most other elements, we can just use the id attribute.

For example, if we have a paragraph tag, <P>, that we want to access via script, we write the following:

<P id="myPara">A Paragraph</P>

To access this in our JavaScript code, we simply use the id value. For example, to retrieve the text inside the paragraph, we would use the P object's innerText property with the code.

myPara.innerText;

This will be "A Paragraph" in this example. We will talk about the innerText property in more detail later in this chapter.

Another way of accessing the objects associated with tags is the document object's all array property (or, more precisely, collection property). This property contains all the elements defined in the source code for the page in source code order. We can reference an element by using its index in the array, or its id value in square brackets after the all word. For example, to reference the previous paragraph we could use the following:

document.all["myPara"];

However, for our purposes in this chapter, it is easier to use the former way of referencing tags' objects.

Changing Appearances

The key to changing the look of an HTML tag after the page has been loaded is the style object, which almost every tag's object has as a property. The style object is JavaScript's representation of a tag's style sheet, which we saw earlier, and most properties we can set with style sheets can also be set using the style object. One example of a property of the style object is color. Quite obviously, this can be used to change the color of an object. For example, to change the color property of the style object property of a paragraph with id of myPara, we would use the following:

myPara.style.color='red';

This technique of using the style object property of the object representing a particular tag to change the tag's appearance works for most tags, and a wide range of appearance changes are available. Everything, from background color to text spacing, text size, and even whether the tag is visible to the user, can be determined using the style object.

It's important to note, however, that accessing a style property won't work if the style is set via a style sheet like this:

 <style type="text/css">#aP { color: white; background-color: darkblue; }</style>

We also have a tag that uses it, such as

 <p id="aP"> Some text</p>

If we then try to access its color property as

document.all.aP.style.color

we'd expect it to return white. However, that is not the case because the style property of an element object relates only to the inline style set on an element and not to any properties set in embedded or external style sheets.

Try It Out – Using the style Object

Let's work with a simple example using the style object.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<script language=JavaScript type="text/javascript">
function myPara_onmouseout()
{
   myPara.innerText = "Roll your mouse over my text";
   myPara.style.color = "Black"
}
function myPara_onmouseover()
{
   myPara.innerText = "Wow that feels good!!!";
   myPara.style.color = "Red"
}
</script>
</head>
<body>
<P id="myPara"
   onmouseout="return myPara_onmouseout()"
   onmouseover="return myPara_onmouseover()">
      Roll your mouse over my text
</P>
</body>
</html>

Save this as rollover_IE.htm. When we load it into our browser, and we should see a paragraph with the text "Roll your mouse over my text," as shown in Figure 12-4.


Figure 12-4

When we do this, the color of the text changes to red, and the text message changes to "Wow that feels good!!!" (See Figure 12-5.)


Figure 12-5

When we move our mouse off of the text, the text returns to the original color and message.

How It Works

In the body of the page, we define a paragraph with id of myPara. We connect the onmouseout event handler for this paragraph to the function myPara_onmouseout() and the onmouseover event handler to the function myPara_onmouseover(). These functions are defined in a script block in the head of the page.

The myPara_onmouseover() function, which is called when the user's mouse pointer rolls over the paragraph, starts by changing the text of the paragraph. It does this using the innerText property that we saw previously.

function myPara_onmouseover()
{
   myPara.innerText = "Wow that feels good!!!";

Then, the function uses the color property of the style object property of the myPara object to change the color of the paragraph.

   myPara.style.color = "Red"
}

The myPara_onmouseout() function, which is called when the user's mouse pointer rolls off the paragraph, has very similar functionality. It simply changes the text to its original value, and changes the color property of the style object to black.

Positioning and Moving Content

We saw earlier how we can use style sheets to determine the exact position of HTML elements on a page when it's loaded. Well, using JavaScript we can move these absolutely or relatively positioned elements around after the page has loaded, again using the style object. In this case we can use the style object's left and top properties to change the left and top positions of HTML elements.

Try It Out – Moving Text

Let's see an example of how to use JavaScript to move the content around after it has loaded. In this case, we're going to make two paragraphs scroll from left to right, then back again in a continuous loop. We'll use the timer functionality we saw in Chapter 9 to keep the tags moving.

<html>
<head>
<script language=JavaScript type="text/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;
      }
   }
   para1.style.left = paraOneLeft + "px";
   para2.style.left = paraTwoLeft + "px";
}
</script>
</head>
<body language=JavaScript onload="window_onload()">
<P style="position:relative;left:100px;top:30px" id="para1">Para 1</P>
<P style="position:relative;left:400px;top:10px" id="para2">Para 2</P>
</body>
</html>

Save the page as MovingTags_IE.htm and load it into the Internet Explorer 4.0+ browser. Hopefully, the contents of the two tags (Para 1 and Para 2) will scroll smoothly across the screen in opposite directions and back again continuously.

How It Works

In the body of the page, we have two <P> tags. Each has been given a unique id value (para1 and para2) and we use these values to reference the P object associated with each tag.

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

Within the style attribute, we have positioned the paragraphs at the coordinates 100,30 and 400,10. Note that this means that para2 will be above para1.

Note that we've positioned the paragraphs relatively rather than absolutely, even though the two <P> tags are not inside other tags. Why?

Well, this is for browser compatibility. IE 4 does not support the absolute positioning of <P> tags, but it does support relative positioning of them. IE 5.0+ supports absolute and relative positioning for <P> tags, so we would have no problems. In this example, using relative positioning when not inside another tag gives the same effect as absolute positioning does.

An alternative would be to use the <div> tag, which is a container tag; that is, it does not display anything, but allows us to group tags together inside it. This supports absolute and relative positioning in IE 4 and IE 5, so we could have put each paragraph inside a separate absolutely positioned <div> tag and moved the <div> tag instead.

Within the script block in the head of the page, after we've declared three variables that will be used to determine the left positions and the scroll direction of the paragraphs, we then come to the window_onload() function.

function window_onload()
{
   window.setInterval("moveParas()",50);
}

Here we start a timer going using the setInterval() method of the window object, which will call the moveParas() function every 50 milliseconds. The definition of the moveParas() function comes next in the script block. This function is used to move the paragraph's text.

How do we want the paragraph's text moved?

We want the top paragraph, para2, to start with a left position of 400 and then move by 1-pixel decrements to the left until the left position is 100. At that point, we want to switch directions and start moving the paragraph back to a left position of 400 by increasing the left position by 1 pixel until it's 400, whereupon we switch directions again and the process starts again.

We want the bottom paragraph, para1, to do the exact opposite of the top paragraph: We want it to start at a left position of 100, scroll to the right by 1 pixel until it hits a left position of 400, and then switch direction and scroll back until it has a left position of 100.

To achieve this, the first task of the moveParas() function is to determine which way to scroll the paragraphs. It does this by looking at the value contained in the switchDirection variable. If the value is false, we want para1 to scroll right, and if it's true, we want it to scroll left. It was initialized to false at the top of the script block.

In the first if statement, the condition checks whether switchDirection has value of false. If so, it increments the left position of para1 by 1 and decrements the left position of para2 by 1. Then in an inner if statement it checks whether the left position of para1 is 400. If so, it is time to switch the direction of the scrolling, so it sets the value of the variable switchDirection to true.

function moveParas()
{
   if (switchDirection == false)
   {
      paraOneLeft++;
      paraTwoLeft--;
      if (paraOneLeft == 400)
      {
         switchDirection = true;
      }
   }

Next, in an else statement, we have similar code for the situation where switchDirection is true, that is, the scrolling is in the opposite direction.

   else
   {
      paraOneLeft--;
      paraTwoLeft++;
      if (paraOneLeft == 100)
      {
         switchDirection = false;
      }
   }

Finally we set the left position of the paragraphs in exactly the same way we set the color in our previous example: by using each P object's style object property.

   para1.style.left = paraOneLeft + "px";
   para2.style.left = paraTwoLeft + "px";
}

That completes the moveParas() function, and the script block.

We'll be looking at moving tags again when we look at the DHTML menus example later in this section.

Changing, Adding, and Deleting Existing Content

We've already seen how in earlier browsers a limited form of DHTML allowed the changing of images loaded in an <img> tag even after the page had completed loading (see Chapter 5). Now, with DHTML in IE 4.0+ we can change all the content after it has loaded.

For IE browsers, three properties and two methods are available for the objects of most tags that are useful for changing content. These are innerText, innerhtml, outerhtml, outerText, insertAdjacentText(), and insertAdjacenthtml().Once the page has completed loading, that is, once the onload event handler of the window object has fired, we can use these properties and methods to change the page's contents.

We'll now look at these properties and events in detail, starting with the innerText property, which we have already seen in use.

Getting and Setting the innerText Property

The property innerText allows us to retrieve, change, or set the plain text contained between a start tag and an end tag, for example <P> and </P>.

Although many tags support this property, it's not always obvious exactly what is returned until we use innerText. For example, although it would work with a <table> tag, it would show all the text inside all the contained <td> tags as the retrieved innerText.

Try It Out – The innerText Property

Let's return to a paragraph example, and use it to examine the innerText property.

<html>
<body>
<P id="para1">Para 1</P>
<br>
<input type="text" id=text1 name=text1 SIZE=50>
<br>
<input type="button" value="Get innerText" id="bttnGetInnerText"
   onclick="text1.value = para1.innerText">
<input type="button" value="Set innerText" id="bttnSetInnerText"
   onclick="para1.innerText = text1.value">
</body>
</html>

Save the example as innertext_IE.htm; then load it into your IE browser. A page with the content shown in Figure 12-6 appears.

Click To expand
Figure 12-6

Click the Get innerText button, and the contents of the paragraph shown above the text box, initially Para 1, are displayed in the text box.

Type something into the text box, and then click the Set innerText button. The text inside the paragraph will change to whatever we added inside the text box.

Try also typing some HTML into the text box (such as <strong>Hello</strong>) and clicking the Set innerText button. Note that the browser does not parse the HTML: It deals with it as though it were plain text.

How It Works

At the top of the page, we define our paragraph tag and make sure it has a unique id value. That way, we can reference it later from JavaScript.

<P id="para1">Para 1</P>

We then define two buttons. The first button, Get innerText, has the following code attached to its onclick event handler, which fires when the button is clicked.

text1.value = para1.innerText

This sets the value of the text box to the value of para1's innerText property.

When clicked, the second button, Set innerText, changes the text inside the paragraph of the document loaded (not the actual HTML file on our disk drive) setting its value to the value in the text1 text box. It does this because the following code is attached to its onclick event handler.

para1.innerText = text1.value

Getting and Setting the innerhtml and outerhtml Properties

Whereas the innerText property gives access to only plain text inside a tag, the innerhtml property allows us to retrieve and change all the contents of a tag, including any HTML. The outerhtml prop-erty allows us to retrieve or change all the contents inside a tag, but this time it also includes the HTML defining the tag itself.

For example, look at the following code:

<div ID=div1>Some text <h2>A heading</h2></div>

The value of the innerhtml property for div1 would be as follows:

Some text <h2>A heading</h2>

The value of the outerhtml property for div1 would look like this:

<div id=div1>Some text <h2>A heading</h2></div>

When we change the HTML using these properties, IE reparses it and displays it as if it had been part of the page when it first loaded.

The innerhtml and outerhtml properties behave differently depending on the type of tag they are used with.

Certain tags are termed container or grouping tags. Their purpose in life is to group other tags. Examples of such tags include the <div> tag and the <span> tag. However, the majority of tags are not actually container tags, even though other tags can be placed inside them. In particular, the <P> tag is not a container tag, even though you could put other HTML inside it, for example <P>Some <strong>Bold</strong> text</P>.

A second and very important distinction that needs to be made is between block-level and inline elements. The W3C defines block and inline elements quite well. The full description is located at http://www.w3.org/TR/REC-html40/struct/global.html#h-7.5.3. However, in brief it says

"Some HTML elements that may appear inside the body of the document are said to be 'block-level' while others are 'inline' (also known as 'text level'). The distinction is founded on several notions:

  • Most block-level elements may contain inline elements and other block-level elements. Most inline elements may contain only data and other inline elements.

  • By default, block-level elements are formatted differently from inline elements. Most block-level elements begin on new lines, whereas inline elements do not."

The sort of elements that are block-level elements in IE include the following:

<address>, <blockquote>, <body>, <DD>, <div>, <DL>, <DT>, <fieldset>, <form>, <frame>, <frameset>, <H1>, <H2>, <H3>, <H4>, <H5>, <H6>, <iframe>, <noscript>, <noframes>, <object>, <OL>, <P>, <UL>, <applet>, <center>, <dir>, <HR>, <menu>, <pre>, <LI>, <table>, <TR>, <thead>, <tbody>, <tfoot>, <col>, <colgroup>, <TD>, <TH>, and <caption>.

The distinction between container and non-container tags, and block and inline elements is important in DHTML, particularly when dealing with the innerhtml and outerhtml properties. With most inline elements, we can use these properties to insert formatting HTML tags, for example <I> for italics and <strong> for a bolder typeface, as these are inline elements. However, we can't insert block-level elements. Most block-level elements can have any valid HTML inserted inside them using the innerhtml and outerhtml properties.

This is true of most block-level elements, but there are exceptions to the rule. For example, although the <P> tag is a block-level element, we can't insert other block-level elements inside it, only inline elements.

For example, if we have a <P> tag, we could insert the following:

<B>Some plain and <I>italicized</I> text with boldness</B>

But because of the block-level <h2> tag, we can't insert this:

Some text, then a <h2>Heading</h2>

If you try this you'll find yourself getting strange and unpredictable results.

In reverse, the inline element <span> can have other inline elements and block-level elements inserted.

In general the two container tags, <div> and <span>, are safe choices when you want to insert any valid HTML.

Try It Out – Viewing and Setting Page HTML

Let's look at an example that demonstrates the innerhtml and outerhtml properties, and the container, non-container, inline, and block-level tags.

<html>
<head>
<script language=JavaScript type="text/javascript">
function sethtml()
{
   var actOnTag = div1;
   if (radTag[0].checked == true)
   {
      actOnTag = para1;
   }
   if (radInnerOuter[0].checked == true)
   {
      actOnTag.innerhtml = textarea1.value;
   }
   else
   {
      actOnTag.outerhtml = textarea1.value;
   }
}
function gethtml()
{
   var actOnTag = div1;
   if (radTag[0].checked == true)
   {
      actOnTag = para1;
   }
   if (radInnerOuter[0].checked == true)
   {
      textarea1.value = actOnTag.innerhtml;
   }
   else
   {
      textarea1.value = actOnTag.outerhtml;
   }
}
</script>
</head>
<body>
<div id="div1">
   <P id="para1">This is a <I>paragraph</I>.</P>
   <h2>A Heading inside the div</h2>
</div>
<br><textarea cols=60 rows=10 ID=textarea1 name=textarea1></textarea>
<br>
Paragraph <input type="radio" name="radTag" checked>
Div <input type="radio" name="radTag">
<br>
Innerhtml <input type="radio" name="radInnerOuter" checked>
outerhtml <input type="radio" name="radInnerOuter">
<P>
<input type="button" value="Get html" name="bttnGetInnerText"
   onclick="gethtml()">
<input type="button" value="Set html" name="bttnSetInnerText"
   onclick="sethtml()">
</P>
</body>
</html>

Save the example page as innerOuterhtml_IE.htm.

Loaded into the browser the page should look like the one shown in Figure 12-7.

Click To expand
Figure 12-7

We are going to try to view or change two important tags in the page with innerhtml and outerhtml properties. The first is div1, which contains a paragraph and a heading. The second is the paragraph inside div1, which contains some text.

<div ID="div1">
   <P ID="para1">This is a <I>paragraph</I>.</P>
   <h2>A Heading inside the div</h2>
</div>

The first set of radio buttons on the page allows us to select which of the two elements, <div> or <P>, we want to act on.

The second set of radio buttons allows us to choose whether it's the innerhtml or outerhtml property we are getting or setting.

Make sure the radio buttons for the Paragraph and innerHTML are selected, and then click the Get HTML button. This will put the value of the innerhtml property for the paragraph into the textarea box. We should see this message:

This is a <I>paragraph</I>.

Now select the outerHTML radio button and click the Get HTML button again. This time, we get the following:

<P id=para1>This is a <I>paragraph</I>.<P>

Notice that now we have the opening <P> and closing </P> tag as well as the contents of the innerhtml. Also notice that the <P> is on the second line of the textarea because <P> is a block-level tag and these normally start on a new line.

If we change the radio buttons so that the Div and innerHTML buttons are selected, and we then click the Get HTML button, we'll see a very different set of tags.

<P id="para1">This is a <I>paragraph</I>.</P>
<h2>A Heading inside the div</h2>

We can see that all the HTML tags inside the container <div> tag have been included. If we now click the radio button for the outerHTML and then click the Get HTML button, we see the same tags we saw previously except that the <div> tag itself is included.

<div id="div1"><P id="para1">This is a <I>paragraph</I>.</P>
<h2>A Heading inside the div</h2></div>

We can also use the Set HTML button to set the innerhtml and outerhtml properties.

For example, with the Div and innerHTML radio buttons selected, we can enter any valid HTML for the contents of the <div> tag into the text area and set it using the Set HTML button. However, if we do change the HTML, the existing tags inside the <div> tag will be lost, so unless we define a tag inside <div> with the id of para1, we'll find that access to para1 will be lost and the Paragraph radio button will no longer be of any use and script errors occur.

We'll also face the same problem if we set the HTML for the outerhtml property of the <div> without defining another <div> tag with the id of div1. Setting the outerhtml replaces not just the HTML inside the tag, but also the tag itself.

However, with the Paragraph and innerHTML radio buttons selected, we can enter only plain text or plain text and formatting tags before we set the HTML.

If we select the outerHTML radio button and delete the paragraph, or even give it another id, any attempts to access para1's inner or outer HTML will fail because para1 no longer exists. Indeed, doing so will cause an error similar to that shown in Figure 12-8.


Figure 12-8

I caused the error in the preceding figure by first setting the outerhtml for the paragraph to the following:

<P id=para99>This is a <I>paragraph</I>.</P>

This code replaced para1 with a paragraph with an ID of para99. When I click the Set HTML or Get HTML buttons when the Paragraph radio button is selected, I get the error shown because para1 no longer exists.

We can use the example to try various tags and settings to see what information is returned and which tags can be validly set. Just remember that the container tags <div> and <span> can contain any valid elements, whether block-level or inline, but that the <P> element can contain only inline elements, such as formatting tags.

How It Works

The aim of this example is to experiment with part of the HTML of the document. This piece of HTML occurs at the beginning of the body of the document:

<div id="div1">
   <P id="para1">This is a <I>paragraph</I>.</P>
   <h2>A Heading inside the div</h2>
</div>

Below this we define a textarea control, with the name and id of textarea1.

<br><textarea cols=60 rows=10 id=textarea1 name=textarea1></textarea>

Next we have two sets of radio buttons. The first two refer to the tag that we want to change. The Paragraph radio button refers to the <P> tag with the ID of para1. The Div radio button refers to the <div> tag with an ID of div1, which encloses all the other tags shown previously.

Paragraph <input type="radio" name="radTag" checked>
Div <input type="radio" name="radTag">

The second set of radio buttons refer to the innerhtml and outerhtml properties.

Innerhtml <input type="radio" name="radInnerOuter" checked>
outerhtml <input type="radio" name="radInnerOuter">

Finally, we have two buttons to get the HTML or set the HTML. Each button's onclick event handler is connected to a function, either the gethtml() or the sethtml() function, defined in the head of the page.

<input type="button" value="Get html" name="bttnGetInnerText"
   onclick="gethtml()">
<input type="button" value="Set html" name="bttnSetInnerText"
   onclick="sethtml()">

Let's now look at the sethtml() function, which we use to set inner or outer HTML properties. The function starts by declaring a variable actOnTag and initializing it to the value div1. Next, if the first of the first set of radio buttons has been checked (the Paragraph radio button), the actOnTag variable's value is changed to para1. Thus this actOnTag variable contains the ID of the tag that we want to view or change.

function sethtml()
{
   var actOnTag = div1;
   if (radTag[0].checked == true)
   {
      actOnTag = para1;
   }

The second if statement looks to see whether radInnerOuter[0], the innerHTML radio button, is checked. If it is, we set the innerhtml property of the tag referenced by the actOnTag variable to the value of the textarea control. If not, then the outerhtml property needs to be set.

   if (radInnerOuter[0].checked == true)
   {
      actOnTag.innerhtml = textarea1.value
   }
   else
   {
      actOnTag.outerhtml = textarea1.value;
   }
}

The gethtml() function works in an similar way, the difference being in those lines in which we set the HTML using innerhtml or outerhtml properties. We instead get their value and write it out to the <textarea> tag by using its value property.

Using the Methods insertAdjacentText() and insertAdjacenthtml()

The three properties we have looked at so far can be used to retrieve or set content in the page. In comparison, the methods insertAdjacentText() and insertAdjacenthtml() allow us to add to the content in the page that already exists.

As with the innerText and innerhtml properties, the difference between the insertAdjacentText() and insertAdjacenthtml() methods is that for the latter any HTML that is added is parsed and displayed as though it were in the page when it first loaded, and with the former any addition to the content is simply inserted as plain text.

As with the innerhtml and outerhtml properties, generally speaking we can insert only inline HTML elements inside other inline elements, and inside block-level elements, we can insert other block-level elements and inline elements. However, again the exceptions to this are tags such as a <P>, which is a block-level element but can have only inline elements inserted. Both the container tags, <span> and <div>, can contain block-level and inline elements even though <span> is an inline tag.

With either of the two insertion methods, we can insert content at any one of four points in relation to the tag with the object's insert method we're using. Both methods take two parameters: The first specifies where we want the new HTML to be inserted, and the second contains the content to be inserted. The first parameter can take one of four values:

  • beforeBegin. This inserts the text or HTML immediately before the start tag.

  • afterBegin. This inserts the text or HTML after the start tag, but before all other content in the element.

  • beforeEnd. This inserts the text or HTML immediately before the closing tag, but after all other content in the element.

  • afterEnd. This inserts the text or HTML immediately after the end of the closing tag.

As an example, let's imagine we have the following tags:

<div id=div1>
   <P>
      text inside a paragraph
   </P>
</div>

Using this HTML, we'll use the insertAdjacenthtml() method to insert a new <br> tag into the <div> tag at each of the four possible positions.

div1.insertAdjacenthtml("beforeBegin", "<br>");

The preceding code results in the following:

<br>
<div id=div1>
   <P>
      text inside a paragraph
   </P>
</div>

In this result, the <br> tag is placed immediately before the starting <div> tag.

Say the first parameter is afterBegin.

div1.insertAdjacenthtml("afterBegin", "<br>")

We get the following results:

<div id=div1>
<br>
   <P>
      text inside a paragraph
   </P>
</div>

We can change the first parameter to beforeEnd.

div1.insertAdjacenthtml("beforeEnd", "<br>")

The HTML will look like this:

<div ID=div1>
   <P>
      text inside a paragraph
   </P>
<br>
</div>

Finally, we could set the first parameter to afterEnd.

div1.insertAdjacenthtml("afterEnd", "<br>")

We get the following results:

<div ID=div1>
   <P>
      text inside a paragraph
   </P>
</div>
<br>

DHTML Events and the IE event Object

In addition to providing lots of new methods and properties for every tag in the page, IE provides the tags' objects with many more events and event handlers. These include ondragstart, which fires when the user starts selecting content with a mouse drag, onhelp, which fires when the user presses F1 or selects Help from the browser menu, and onscroll, which fires when the user has adjusted an element's scroll bar. However, we will not look at these events in more detail in this book.

The versions of IE from 4.0 onward also redefine the chain of events, that is, the order in which events fire, so that events bubble up from the innermost tag to the very outer tag. For example, if we have a table that is contained within a <div> tag in a page, and the user moves the mouse pointer over a table cell, the chain of events will be the onmouseover events for the following tags: First the mouse pointer moves over the <td> table cell, next the <tr> tag that the <td> is contained within, then the <table>, followed by the <div>, and finally the document object. At each point the mouseover event fires for each element and we can add code that runs for any one of these events or all of them if we wish.

DHTML under IE 4.0+ also provides the event object. This object is a property of the window object, and one such object exists for each open browser window. It provides us with lots of very useful information about the most recent event to fire, such as details regarding the event that was fired, the tag that caused the event to fire, information about where the user's mouse pointer is, what buttons or keys were pressed, and much, much more.

The event object has a lot of properties and methods, but we are going to concentrate on only a few of the more useful ones. We'll take a look at these in the following sections.

Why Did the Event Fire?

The event object allows us to find information about what the user did to cause the event to fire. For example, we can check to see where the mouse pointer is, which mouse buttons have been clicked, and which keys have been pressed.

The properties screenX and screenY of the event object provide us with the coordinates of the user's mouse pointer at the time of the event. Because the event object is already created for us, we simply use it by typing its name followed by the property or method we're interested in.

event.screenX

The preceding entry gets us a reference to the horizontal position in pixels of the user's mouse pointer.

Contrast this with the following code:

event.screenY

This gets us a reference to the vertical position in pixels of the mouse pointer. Remember that screen coordinates start at 0,0 in the top-left corner of the screen.

The button property of the event object tells us which mouse button, if any, the user has pressed. It returns a number between 0 and 7 (inclusive), which indicate the following:

  • 0: No button is pressed.

  • 1: Left button is pressed.

  • 2: Right button is pressed.

  • 3: Left and right buttons are both pressed.

  • 4: Middle button is pressed.

  • 5: Left and middle buttons are both pressed.

  • 6: Right and middle buttons are both pressed.

  • 7: All three buttons (left, right, and middle) are pressed.

Finally, we can find out whether a key has been pressed and which key it was by using the keyCode property of the event object. This returns a number that indicates the Unicode character code corresponding to the key pressed. If no key was pressed, this will be zero.

Finding Out Which Tags Are Involved in the Event

The event object has three properties: fromElement, srcElement, and toElement. These properties provide a reference to the objects of the tags involved in the event. For example, in an onmouseover event the following conditions apply:

  • The fromElement property will refer to the object of whichever tag the user's mouse pointer was on before moving over the tag that fired the onmouseover event.

  • The srcElement property will be the object of the tag that caused the event to fire. For example, if the mouse pointer just rolled over an image, the srcElement will be that <img> tag's object.

  • The toElement property is the object of the tag that the user's mouse pointer is about to move to, for example in an onmouseout event. How does the browser know where the mouse is going? Well, it raises the events for a particular user action after they have actually happened, although the delay is only microseconds. It's rather like a live TV broadcast that is actually transmitted with a short delay so that swear words can be bleeped out.

Each of these properties provides the actual object of the tag, which we can use and manipulate as if we had referenced the tag directly. It can be particularly useful in writing generic code that is placed in the event of a higher object, such as document.

Dynamic Menus in IE

We've taken a brief tour of DHTML in IE, so now it's time to put it into practice with an example that includes everything we've seen so far.

In this example, we build a web page with three images on it representing menu choices. When the user's mouse pointer rolls over an image, a menu will appear to the right of the image with various menu options from which the users can choose to go to a new page. (See Figures 12-9 and 12-10.)

Click To expand
Figure 12-9
Click To expand
Figure 12-10

When the user's mouse pointer rolls off the menu, it disappears again.

The IE DHTML Menu System Principles

Let's start by taking a brief high-level overview of how the menu system works. Once we have that explained, we'll look at the HTML and code in much more depth.

The principle is very simple. For each main menu option—that is, Wood, Metal, and Bricks—we have an image. For each main menu option, we also have a <div> tag, which contains a list of menu items. For example, for wood we have Oak Timber, Teak Timber, Pine Timber, and Yew Timber. We capture the mouseover and mouseout events of the image. Remember that this is IE, so unlike NN the <img> tag does have these events and there is no need to wrap it in an <A> tag. When the mouse rolls over the image, we display <div> with its menu items. When the mouse leaves the image, and is not over <div>, we hide <div>.

When the page first loads, the <div> tag is actually located off-screen by setting its left style attribute to a large negative number. Concentrating on just the wood menu image and <div>, the page screen at start up looks like Figure 12-11.


Figure 12-11

Now the user rolls the mouse pointer over the wood image causing the image's mouseover event to fire, in which case we move <div> from its off-screen position to a position next to the image, as shown in Figure 12-12.

Click To expand
Figure 12-12

If the user's mouse pointer moves off the image, we want to hide <div> by moving it back to its off-screen position. We can do this in the onmouseout event handler. However, if the mouse pointer moves to the right of the image and over <div> containing the menu items, we don't want to move <div>. A menu that disappears as soon as you put your mouse pointer over it would be fairly pointless. (See Figure 12-13.)

Click To expand
Figure 12-13

So if the user's mouse pointer is moving from the image to <div>, we won't hide <div>.

If the mouse pointer moves off the <div> to the rest of the page, the code we add to the <div> onmouseout event handler will hide the<div>, again by setting it to an off-screen position.

However, there is a slight problem. When the user's mouse pointer moves from the edge of <div> to the next element inside <div>, IE considers that the mouse pointer is no longer over <div> but over a tag inside <div> and promptly fires the <div>'s onmouseout event handler code. The user is trying to select a menu item inside <div>, so again we don't want to hide <div>. We only hide <div> when the mouse pointer moves from <div> to an element not contained inside <div>.

In summary, we show <div> if the mouse pointer moves over the image.

We hide <div> in either of the following two circumstances:

  • The mouse pointer moves off the image but not onto <div>

  • The mouse pointer moves off <div>, but not to a tag inside <div>

We've covered how the menus appear and disappear, but how do we make each menu item in <div> change font and background colors when the mouse rolls over it? Easy. We simply capture the onmouseover and onmouseout events for each element inside <div>. When the pointer rolls over the element, we change the element's style to the blue background and white text shown in Figure 12-12. When it moves off the element, we change the style back to its original look. It's very similar to the example we saw earlier in the chapter when we changed the text and appearance of a paragraph when the mouse pointer rolled over or out of it.

Finally, when an element containing the menu item is clicked, we capture the onclick event handler and take the user to the page for that item.

Now that we have a good idea of how the basic principles work, it's time to create our first, simple example of the system in which we'll explore the HTML and code necessary for the basic functionality in much more detail. Later we'll modify the simple example, so that all the code is created dynamically, based on information in an array.

DHTML Menus Simple Version

In the final version of this example we'll be creating the DHTML using code. However, it's easier to understand the DHTML if we first look at just the DHTML itself. Then we'll look at the style sheets we need, and finally at the basic code required.

We'll first create a simplified version of the menu system, one containing just one menu: the Wood menu. We'll look at the DHTML we'll need; then we'll look at the style sheets needed, and finally we'll look at the code required. Then we'll put it all together in a simple example. Once we've done that, we should have a good idea of how the dynamic menu system works and we can create the final version, which not only contains more menus, but also creates the HTML dynamically based on information stored in arrays. That way, adding a menu item or adding a whole new menu requires changes to the array and possibly an additional image, but no changes to the code.

The DHTML

We'll concentrate on the DHTML required for just the Wood menu. We have an image on the page saying "Wood". When the user's mouse pointer rolls over the image, our menu appears. When it leaves the image or menu, the menu disappears. Let's look at the image tag itself.

<img id="WoodMenuImage"
   src="WoodButton.gif"
   style="position:absolute;left:10;top:75"
   onmouseover="showMenu(WoodMenuDiv)"
   onmouseout="hideMenu(WoodMenuDiv)">

The <img> tag is simple enough. It has an ID of "WoodMenuImage" and we've positioned it absolutely. We've set the onmouseover event handler to call a function showMenu() and the onmouseout event handler to call hideMenu(). We'll be creating and looking at how these functions work later, but for now we'll stick with looking at what they do. The showMenu() function shows our popup menu, but what is it actually showing? Well, it takes just one parameter and this parameter is a reference to the Div object that contains all the menu items. Here we're passing WoodMenuDiv, which is the object representing our Wood menu <div>, which we'll look at in a minute. The function hideMenu() is simply the opposite of showMenu() in that it hides the menu by hiding the <div>. Again, we pass a Div object, our WoodMenuDiv.

We've been talking about showing and hiding a <div>, so let's look at the HTML for this <div> for the Wood menu:

<div id="WoodMenuDiv" class="DivMenu"
   onmouseout="return hideMenu(this)">
</div>

Again this is simple enough. We've given it a unique id. Its style is defined in a style class called DivMenu, which we'll look at shortly, and the onmouseout event handler calls the same hideMenu() function that the <img> tag's onmouseout event handler did. This function is passed the same parameters, although on this occasion we pass this as the Div object to hide. The this word is JavaScript's way of saying this object. In the context of an event handler, this always refers to the object of the tag the event handler is part of. In our case this refers to the object representing the <div> tag. We could have written WoodMenuDiv as the parameter, and that would have passed exactly the same thing, the Wood menu <div> tag's object. Using this is just convenient shorthand and because it is non-specific, we can use the same code in different places. For example, this in the <div> tag for the Metal menu will refer to that Div object.

The reason we call the hideMenu() function in the <div> tag's onmouseout event handler is that we want to hide the menu if the user's mouse pointer leaves the menu, as well as when it leaves <img>.

Okay, we have a <div> for the menu, but there's nothing inside it. Currently it's a blank menu, so let's add each of our menu items for the Wood menu: Oak Timber; Teak Timber; Pine Timber; Yew Timber.

The <div> and the HTML we need inside it are shown in the following code:

<div id="WoodMenuDiv" class="DivMenu"
   onmouseout="return hideMenu(this)">
<table border=0 cellspacing=1 cellpadding=1 id="WoodTable">
   <TR>
      <TD id="WoodOak" RollOver RollOut
         onclick="goPage('OakWood.htm')"
         class="TDMenu">
            Oak Timber
      </TD>
   </TR>
</table>
</div>

So far I've just added the menu item for Oak Timber. We're putting the menu items inside a table for formatting reasons and because it gives us a specific element, the <TD>, whose events we can use. The <TD> tag's onclick event handler calls the goPage() function we'll create later that takes the user to the page for that menu item, for example the OakWood.htm page.

Notice something rather strange in the <TD> tag's definition: RollOver and RollOut. The <TD> tag does not have those attributes, so what are they doing there? Surely this is not valid HTML.

The <TD> tag does not have these attributes. I just made them up. However, there is some logic to it. As far as the HTML goes, the RollOver and RollOut attributes will be ignored. IE will take one look at them when creating the page, not recognize them as valid HTML attributes, and ignore them as far as displaying HTML goes. However, it does not completely ignore them. When IE is creating the objects representing those <TD> tags, it will add RollOver and RollOut as properties of the TD object—properties we can use in our JavaScript. For our code, we're not interested in whether the attributes have values, but simply whether they exist, but why should we care whether they exist?

The RollOver and RollOut properties mark those <TD> tags as being tags that we want to change the style of. More precisely, when the user's mouse pointer rolls over them, we want to change their style, and when the user's mouse pointer rolls out of them, we want to change their style back again. If it's a <TD> tag and does not contain those properties, there is no need to change the style when the user rolls over/rolls out with the mouse pointer. The reason we need to do this in the first place is that the code we'll be adding later to do the roll over and roll out style changes is actually in the document object's onmouseover and onmouseout event handlers. We'll be putting it there because it saves us from having to write an event handler for every <TD> menu tag in the page. Instead we'll let the onmouseover and onmouseout events bubble up from the <TD> tags and capture them in the document. If the <TD> tags have the RollOver and RollOut properties, we'll change their style; otherwise we'll just ignore the event. We'll see in more detail how this works when we look at the code.

We've added only one menu item so far, so let's now add the rest for the Wood menu.

<div id="WoodMenuDiv" class="DivMenu"
   onmouseout="return hideMenu(this)">
<table border=0 cellspacing=1 cellpadding=1 id="WoodTable">
   <TR>
      <TD id="WoodOak" RollOver RollOut
         onclick="goPage('OakWood.htm')"
         class="TDMenu">
            Oak Timber
      </TD>
   </TR>
   <TR>
      <TD id="WoodTeak" RollOver RollOut
         onclick="goPage('TeakWood.htm')"
         class="TDMenu">
            Teak Timber
      </TD>
   </TR>
   <TR>
      <TD id="WoodPine" RollOver RollOut
         onclick="goPage('PineWood.htm')"
         class="TDMenu">
            Pine Timber
      </TD>
   </TR>
   <TR>
      <TD id="WoodYew" RollOver RollOut
         onclick="goPage('YewWood.htm')"
         class="TDMenu">
            Yew Timber
      </TD>
   </TR>
</table>
</div>

A pattern is emerging in that all the <TR> and <TD> tags are identical except for their id values and, in the case of the <TD> tags, the page that they go to when clicked. That's all of the dynamic part of the HTML created, so let's look at the style sheets.

The Style Sheets

The preceding code uses two style classes: DivMenu and TDMenu, that define the look and position of the <div> and <TD> tags used to make up the menus.

<style>
   .DivMenu {
      position:absolute;
      left:-200px;
      top:-1000px;
      width:180px;
      z-index:100;
      background-color: darkorange;
      border: 4px groove lightgrey;
   }
   .TDMenu {
      color:darkblue;
      font-family:verdana;
      font-size:70%;
      width:100%;
      cursor:default;
   }
</style>

We talked about style sheets and classes in the first section of this chapter. Using classes like this helps make page maintenance easier because changing properties for a class of tags, such as our menu items, requires only changes to the style sheet and not to every tag.

The first class, DivMenu, is applied in the preceding code to our <div> menu tags. The style it defines is that the position of the <div> should be absolute, that it should be at left position of -200 and top of -1000. This means that when the page is first loaded, <div> will be off-screen and therefore invisible. Then we set its z-index style attribute to 100. The z-index style attribute specifies how high up in the page the tag will be. By this I mean that if certain tags are positioned on top of each other, the higher the z-index attribute, the more likely it is that the tag will be in front of the other tags. The value 100 has been selected arbitrarily as a value high enough that it's unlikely that anything else on our page will have a z-index higher. Thus, it is unlikely that the <div> will be covered over by any other tag on the page. Finally we've set the background color to dark orange and added a 4-pixel wide light gray grooved border.

The TDMenu class is applied to each <TD> used for a menu item in our menus. It defines the color of any text to be dark blue, its font to be Verdana, and its size to be 70% of the normal font size. We've made sure that the <TD> is 100% of the width of the table and that when the cursor rolls over the <TD> it remains the default cursor, normally a pointer. Without this last attribute, the mouse cursor would be a single line when over a <TD> tag.

Showing and Hiding the Menus

To show and hide the menus, we use the showMenu() and hideMenu() functions. As we have seen, the showMenu() function is connected to the onmouseover event handler of the menu image, and the hideMenu() function is connected to the onmouseout event handlers of the menu image and the menu <div>. First, let's see the finished showMenu().

function showMenu(menuToShow)
{
   var srcElement = event.srcElement;
   var xPos = parseInt(srcElement.offsetLeft);
   var yPos = parseInt(srcElement.offsetTop);
   menuToShow.style.left = xPos + (srcElement.width)
   menuToShow.style.top = yPos;
}

It's surprisingly short and simple. On the first line we declare a new variable, srcElement, and set it to reference the srcElement property of the event object. Whenever an event fires, this global event object is populated with various bits of information. The srcElement property, as we saw earlier, contains a reference to the object of the tag that caused the event to fire. Here we are using this reference to the tag that fired the event to get the position of that tag on the page. We want to position our menu next to the element that invoked the menu shown.

Next we set the xPos and yPos variables to the left and top position values of the element that fired the event by using the offsetLeft and offsetTop properties of the srcElement, our <img> tag's object. These tell us where the left of the image is and where the top of the image is. In fact, it actually tells us how far from the edge of the parent tag the image is offset. Because the images are not inside any tags, the parent tag is the <body> or the document itself, so the offsetLeft and offsetTop properties in this case are simply the screen coordinates of the tags inside the document.

The only parameter of the showMenu() function is the object of the <div> containing the menu we want to show. We use this parameter to move the <div> from its place off-screen to a position on-screen at the same height as the image that invoked it, but directly to the right. The horizontal position is simply the xPos position of the image plus its width, which we can get from our variable srcElement using the width property.

Next we have our hideMenu() function; again it's short and sweet.

function hideMenu(menuToHide)
{
   if (event.toElement != menuToHide &&
      menuToHide.contains(event.toElement) == false)
   {
      menuToHide.style.left = -200;
      menuToHide.style.top = -1000;
   }
}

The hideMenu() function may be called by either the image's onmouseout event handler or the onmouseout event handler of the <div> for each menu. Basically, we want to hide the menu in two situations:

  • The mouse pointer moves off the image, but not onto the <div>

  • The mouse pointer moves off the <div>, but not onto a tag inside the <div>

It is easy to handle it when the mouse is moving off the image or <div>. This is handled as a simple consequence of the hideMenu() function being called by the onmouseout event handlers for the image and the <div>. The hard part is checking the second two conditions: that we are not moving to the <div> and we are not moving to a tag inside the <div>.

The parameter of the function, menuToHide, is the object of the <div> tag we may want to hide. Then the function checks whether it should hide the menu in an if statement. The condition of this statement has two parts. The first part determines that the mouse pointer is not going to the <div> tag. Remember that event.toElement gives the object of the element that the mouse is moving to when the event occurs. The second part of the condition uses the contains() method of a tag's object, which returns true if an element contains another element and false otherwise. If it's false that the menuToHide object contains the element the mouse pointer is moving to, the mouse pointer is certainly not going to any of our menu item elements inside the <div>.

If the condition of the if statement evaluates to true, the function hides the <div> by setting its style object's left and top properties to a position off-screen.

Menu Rollover and Rollout Style Changes

We've seen how the menus are created and why they appear and disappear when the user moves the mouse pointer over or away from them. How do we create the effect of highlighting the text and background of a menu item when the user's mouse pointer runs over it?

The key to this is our capture of the document object's events with the onmouseover and onmouseout event handlers. The document object is not defined by any particular tag, so we can't use our normal method of connecting to events by setting the particular tag's attributes. Instead we need to specify that the document object's onmouseover and onmouseout properties are set to our functions to handle these events; in this case we'll be calling the functions document_onmouseover() and document__onmouseout().

document.onmouseover = document_onmouseover;
document.onmouseout = document_onmouseout;

Here the document_onmouseover() function is connected to the onmouseover event of the document object, and the document_onmouseout() function is connected to the onmouseout event of the document object. We'll be putting these two lines of code in a script block outside of any function so that they're called when the page loads.

We'll look at the document_onmouseover() function first.

function document_onmouseover()
{
   var srcElement = event.srcElement;
   if (srcElement.tagName == "TD" &&
      typeof(srcElement.RollOver) != "undefined")
   {
      srcElement.style.color = "white";
      srcElement.style.backgroundColor ="darkblue";
   }
}

Rather than capture the onmouseover event on every <TD> tag, we've decided to create a generic event handler that captures any onmouseover events of the document object. Because the document is at the top of the BOM hierarchy, it means any events that occur at the lower levels, such as with <TD> tags inside <TR> tags, which are inside <table> blocks, which, in our case, are inside <div> tags, will bubble up to the document. We then just need to check that the source of the event, that is, the srcElement property, was a <TD> tag and confirm that it's one of our menu item <TD> tags rather than from another unrelated table in the page. The srcElement.tagName property tells us what type of tag was at the center of events. If it's TD then we know it's a <TD> tag.

Although this page has no other <TD> tags except for menu ones, it's quite likely that when used in reality we would have various other tables with <TD> tags that we don't want affected by highlighting. How can we determine whether the <TD> is one of the menu item tags?

Well, when the <TD> tags were defined, we added two attributes, RollOver and RollOut, that we can now use to check to see whether this is one of our menu tags. If the srcElement has a property named RollOver whose type is not undefined, we know that this is one of our menu item tags that requires a style change if the user rolls over it. We set the color of the text to white and the background color to darkblue.

The document_onmouseout() function works nearly identically to the document_onmouseover() function. The only difference is the style being changed. This time we want to revert to the default menu colors of darkblue and darkorange.

function document_onmouseout()
{
   var srcElement = event.srcElement;
   if (srcElement.tagName == "TD" && typeof(srcElement.RollOut) != "undefined")
   {
      srcElement.style.color = "darkblue";
      srcElement.style.backgroundColor = "darkorange";
   }
}
Clicking a Menu Item

The final part of the simple version of the DHTML menu system that needs explaining is the onclick event that every menu item responds to. Each <TD> tag has its onclick event handler connected to the goPage() function. This navigates the page to a new source, using the href property of the location object.

function goPage(src)
{
   window.location.href = src;
}

Putting It Together: Simple DHTML Menu Example

Okay, we've seen the components of the simple menus example. Let's now create the whole page.

<html>
<head>
<script language=JavaScript type="text/javascript">
function showMenu(menuToShow)
{
   var srcElement = event.srcElement;
   var xPos = parseInt(srcElement.offsetLeft);
   var yPos = parseInt(srcElement.offsetTop);
   menuToShow.style.left = xPos + (srcElement.width)
   menuToShow.style.top = yPos;
}
function hideMenu(menuToHide)
{
   if (event.toElement != menuToHide &&
      menuToHide.contains(event.toElement) == false)
   {
      menuToHide.style.left = -200;
      menuToHide.style.top = -1000;
   }
}
function document_onmouseover()
{
   var srcElement = event.srcElement;
   if (srcElement.tagName == "TD" && typeof(srcElement.RollOver) != "undefined")
   {
      srcElement.style.color = "white";
      srcElement.style.backgroundColor ="darkblue";
   }
}
function document_onmouseout()
{
   var srcElement = event.srcElement;
   if (srcElement.tagName == "TD" && typeof(srcElement.RollOut) != "undefined")
   {
      srcElement.style.color = "darkblue";
      srcElement.style.backgroundColor = "darkorange";
   }
}
function goPage(src)
{
   window.location.href = src;
}
document.onmouseover = document_onmouseover;
document.onmouseout = document_onmouseout;
</script>
<style>
   .DivMenu {position:absolute;
      left:-200px;
      top:-1000px;
      width:180px;
      z-index:100;
      background-color:darkorange;
      border: 4px groove lightgrey;
   }
   .TDMenu {
      color:darkblue;
      font-family:verdana;
      font-size:70%;
      width:100%;
      cursor:default;
   }
</style>
</head>
<body>
<div id="WoodMenuDiv" class="DivMenu"
   onmouseout="return hideMenu(this)">
<table border=0 cellspacing=1 cellpadding=1 id="WoodTable">
   <TR>
      <TD id="WoodOak" RollOver RollOut
         onclick="goPage('OakWood.htm')"
         class="TDMenu">
            Oak Timber
      </TD>
   </TR>
   <TR>
      <TD id="WoodTeak" RollOver RollOut
         onclick="goPage('TeakWood.htm')"
         class="TDMenu">
            Teak Timber
      </TD>
   </TR>
   <TR>
      <TD id="WoodPine" RollOver RollOut
         onclick="goPage('PineWood.htm')"
         class="TDMenu">
            Pine Timber
      </TD>
   </TR>
   <TR>
      <TD id="WoodYew" RollOver RollOut
         onclick="goPage('YewWood.htm')"
         class="TDMenu">
            Yew Timber
      </TD>
   </TR>
</table>
</div>
<img id="WoodMenuImage" alt="wood_menu"
   src="WoodButton.gif"
   style="position:absolute;left:10px;top:75px"
   onmouseover="return showMenu(WoodMenuDiv)"
   onmouseout="return hideMenu(WoodMenuDiv)">
</body>
</html>

Save this as IEMenus.htm. We will also need an image, WoodButton.gif, for this example and which is included with the downloads for this book. Load it into a browser and roll the mouse pointer over the image and menu that appears. We haven't created the pages for each menu item, so if we click a menu item we'll get a "page not found" error.

The Full IE DHTML Menu Example

We've successfully completed all the DHTML functionality for our example. Now we will adapt the page, so that instead of writing the HTML for each menu <div> by hand, we'll create a function that will, based on information in an array, create all the <div> and its <table>, <TR>, and <TD> tags for us, and finally the function will write the information to the page using document.write().

Although things work fine, we could make life easier for ourselves later if adding, removing, or editing a menu item could be done by simply changing the text in an array. If we want to add a menu item we simply add a few lines to an array. If we want to remove an item, we simply delete a few lines. Editing a menu involves changing only the details stored in the array. It also takes up less space, so it'll be quicker to download and easier to read. The chapters on server-side scripting and databases (Chapters 15 and 16) discuss how we could even store the menus in a database and create them using server-side script.

DHTML Code

It's time to type in the code necessary for the full menu system. The good news is that the changes, although long, are fairly simple to understand. The first change is an addition to the script block in the head of the page.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<head>
<script language=JavaScript type="text/javascript">
var woodMenuItems = new Array();
woodMenuItems[0] = new Array();
woodMenuItems[0][0] = "Oak";
woodMenuItems[0][1] = "OakWood.htm";
woodMenuItems[0][2] = "Oak Timber";
woodMenuItems[1] = new Array();
woodMenuItems[1][0] = "Teak";
woodMenuItems[1][1] = "TeakWood.htm";
woodMenuItems[1][2] = "Teak Timber";
woodMenuItems[2] = new Array();
woodMenuItems[2][0] = "Pine";
woodMenuItems[2][1] = "PineWood.htm";
woodMenuItems[2][2] = "Pine Timber";
woodMenuItems[3] = new Array();
woodMenuItems[3][0] = "Yew";
woodMenuItems[3][1] = "YewWood.htm";
woodMenuItems[3][2] = "Yew Timber";
// Second menu
var metalMenuItems = new Array();
metalMenuItems[0] = new Array();
metalMenuItems[0][0] = "Steel";
metalMenuItems[0][1] = "SteelMetal.htm";
metalMenuItems[0][2] = "Steel Girders";
metalMenuItems[1] = new Array();
metalMenuItems[1][0] = "Copper";
metalMenuItems[1][1] = "CopperMetal.htm";
metalMenuItems[1][2] = "Copper Pipes";
metalMenuItems[2] = new Array();
metalMenuItems[2][0] = "Gold";
metalMenuItems[2][1] = "GoldMetal.htm";
metalMenuItems[2][2] = "Gold Ingots";
var bricksMenuItems = new Array();
bricksMenuItems[0] = new Array();
bricksMenuItems[0][0] = "StdHouse";
bricksMenuItems[0][1] = "StdHousebricks.htm";
bricksMenuItems[0][2] = "Standard House Brick";
bricksMenuItems[1] = new Array();
bricksMenuItems[1][0] = "LargeHouseBrick";
bricksMenuItems[1][1] = "LargeHousebricks.htm";
bricksMenuItems[1][2] = "Large House Bricks";
bricksMenuItems[2] = new Array();
bricksMenuItems[2][0] = "BreezeBlock";
bricksMenuItems[2][1] = "BreezeBlock.htm";
bricksMenuItems[2][2] = "Breeze Block";
function createMenu(menuName, menuItems)
{
   var divhtml = '<div id="' + menuName + 'MenuDiv" CLASS="DivMenu"';
   divhtml = divhtml + ' onmouseout="return hideMenu(this)">';
   var tablehtml = '<table border=0 cellspacing=1 cellpadding=1 ID="'
      + menuName + 'Table">';
   var tableRowhtml = "";
   var rowCount;
   var totalNoRows = menuItems.length;
   for (rowCount = 0; rowCount < totalNoRows; rowCount++)
   {
      tableRowhtml = tableRowhtml + '<TR><TD ID="' +
         menuName + menuItems[rowCount][0] +
        '" RollOver RollOut';
      tableRowhtml = tableRowhtml + ' onclick="goPage(\''
         + menuItems[rowCount][1] + '\')"';
      tableRowhtml = tableRowhtml
         + 'CLASS="TDMenu">' + menuItems[rowCount][2]
         + '</TD></TR>';
   }
   return divhtml + tablehtml + tableRowhtml + '</table></div>';
}
function showMenu(menuToShow)
{

The other changes are within the body of the page.

<body>
<script language=JavaScript type="text/javascript">
   document.write(createMenu('Wood', woodMenuItems))
   document.write(createMenu('Metal', metalMenuItems))
   document.write(createMenu('Bricks', bricksMenuItems))
</script>
<img id="WoodMenuImage"
   src="WoodButton.gif"
   style="position:absolute;left:10px;top:75px"
   onmouseover="return showMenu(WoodMenuDiv)"
   onmouseout="return hideMenu(WoodMenuDiv)">
<img id="MetalMenuImage"
   alt="MetalMenu"
   src="MetalButton.gif"
   style="position:absolute;left:10;top:115"
   onmouseover="return showMenu(MetalMenuDiv)"
   onmouseout="return hideMenu(MetalMenuDiv)">
    
<img id="BricksMenuImage"
   alt="BricksMenu"
   src="BricksButton.gif"
   style="position:absolute;left:10;top:155"
   onmouseover="return showMenu(BricksMenuDiv)"
   onmouseout="return hideMenu(BricksMenuDiv)">
</body>

Resave the page. Also note that three images are needed for this example: WoodButton.gif, MetalButton.gif, and BricksMenu.gif. Create these, or alternatively, retrieve them from the code download for the book.

Load the page into a browser. Rolling the mouse pointer over any of the images causes a menu to pop up with various options. When we pass the mouse pointer over these menu options, notice that the background and text change color. If we select a menu option it will take us to the relevant page. However, because we haven't created any other pages we just get a "page not found" error message.

How It Works

The first thing to notice about the preceding code is that the tags we talked about previously, such as the <div>, <table>, <TR>, and <TD> tags, are nowhere to be found in the page. Instead we've used a function, createMenu(), which takes information from an array and uses this to create the necessary HTML for the menu, which it then writes to the page using document.write(). This way we don't have to do quite as much typing.

Let's start our look at the code by looking at the menu arrays. These appear at the top of the first script block within the head of the page.

For each menu we have a separate array. Each array follows the same pattern and principles, so we'll just look at the first menu array, woodMenuItems[].

We first declare the variable woodMenuItems and set it to reference a new Array object.

var woodMenuItems = new Array();

This array is going to be a multi-dimensional array. Therefore every element woodMenuItems[x] will also be declared as an array. Each of these inner arrays will hold information for a particular menu item.

We start this process with the array element of index 0.

woodMenuItems[0] = new Array();

Now we can use the elements of this array, namely woodMenuItems[0][0], woodMenuItems[0][1], and woodMenuItems[0][2], to hold information about the first menu item. That information will be the name of the menu item, the page to be loaded if the menu item is clicked, and finally the text to be displayed in the menu. So we see that the following code holds the name of the menu.

woodMenuItems[0][0] = "Oak";

The next line holds the page to be loaded.

woodMenuItems[0][1] = "OakWood.htm";

And the following line holds the text to be displayed in the menu.

woodMenuItems[0][2] = "Oak Timber";

For the remainder of the menu items for the Wood menu we follow the same pattern of defining a new Array object and inserting it in the first dimension. Then we make the second dimension of the array hold the name, the page to load if the item is clicked, and text to display for that menu choice.

woodMenuItems[1] = new Array();
woodMenuItems[1][0] = "Teak";
woodMenuItems[1][1] = "TeakWood.htm";
woodMenuItems[1][2] = "Teak Timber";
woodMenuItems[2] = new Array();
woodMenuItems[2][0] = "Pine";
woodMenuItems[2][1] = "PineWood.htm";
woodMenuItems[2][2] = "Pine Timber";
woodMenuItems[3] = new Array();
woodMenuItems[3][0] = "Yew";
woodMenuItems[3][1] = "YewWood.htm";
woodMenuItems[3][2] = "Yew Timber";

Although not shown here, the remainder of the menus for metalMenuItems and bricksMenuItems follow the same principles.

The function that makes use of the arrays to create the menus is the aptly named createMenu() function. This comes next in the first script block in the head of the page. This function creates the same HTML we saw previously when discussing the principles of the menu system, but by using arrays and a function like this we make it much easier to create new menus or change existing menus. All that is involved in creating a new menu is to create the <img> tag for the menu. We then add the menu details to a new array and, finally, call the createMenu() function.

The createMenu() function starts by building up the HTML for the start tag for the <div> and <table> inside the divhtml and tablehtml variables. We use the menuName parameter passed to the function to create a unique id values for the <div> and <table> HTML.

function createMenu(menuName, menuItems)
{
   var divhtml = '<div ID="' + menuName + 'MenuDiv" CLASS="DivMenu"';
   divhtml = divhtml + ' onmouseout="return hideMenu(this)">';
   var tablehtml = '<table border=0 cellspacing=1 cellpadding=1 ID="'
      + menuName + 'Table">';

Next we loop through the items in the menuItems array, passed as the function's second parameter, which will be one of the three arrays created previously. We create the <TR> and <TD> tags necessary for each menu item.

   var tableRowhtml = "";
   var rowCount;
   var totalNoRows = menuItems.length;
   for (rowCount = 0; rowCount < totalNoRows; rowCount++)
   {
      tableRowhtml = tableRowhtml + '<TR><TD ID="' +
         menuName + menuItems[rowCount][0] +
        '" RollOver RollOut';
      tableRowhtml = tableRowhtml + ' onclick="goPage(\''
         + menuItems[rowCount][1] + '\')"';
      tableRowhtml = tableRowhtml
         + 'class="TDMenu">' + menuItems[rowCount][2]
         + '</TD></TR>';
   }

Finally, we join the start tags for the <div> and <table>, the <TR> and <TD> tags created for the menu items, and the close tags for the <table> and <div>, and return these to the code that called the function.

   return divhtml + tablehtml + tableRowhtml + '</table></div>';
}

Next, within a script block at the top of the body of the page, we write the menus to the page using document.write(), which calls createMenu() to provide the HTML. This will insert exactly the same DHTML we saw in the earlier simple version of this example. The DHTML consists of a <div> for each menu; that is, wood, metal, and bricks, with each <div> containing a table, and each row containing a menu item. The only change is that now we have the HTML for the metal and bricks menus too.

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

Following this, we also define an <img> tag for each of our menu buttons. The wood one was there before, but we've now added one for the Metal menu and one for the Bricks menu, using exactly the same principles.


Team LiB
Previous Section Next Section


JavaScript Editor JavaScript Validator     JavaScript Editor


©