Special Edition Using HTML 4

Previous chapterNext chapterContents


- 19 -
Scripting the Object Model

by Jerry Honeycutt

Introducing Dynamic HTML

Dynamic HTML is the next great Web wave. For the uninitiated, it includes a combination HTML, styles, and the scripts used to manipulate both.

The differences between Microsoft's and Netscape's dynamic HTML are enormous. So much so that writing a dynamic HTML Web page that's cross-platform compatible is almost impossible. The biggest difference between both versions of dynamic HTML is the object model. Microsoft's object model goes much farther than Netscape's. It exposes every single element on the page, supporting a variety of properties, elements, events, and collections for each. Microsoft's object model comes closer to what I expect W3C to define as a recommendation later. Thus, all the examples in this book work with Internet Explorer, but some don't work with Communicator.

You can learn more about each company's object model at its respective Web site. You can also learn more about W3C's impending document object model recommendation on its Web site. Here are the URLs for your convenience:

http://www.microsoft.com/msdn/sdk/inetsdk/help/dhtml/ref_objects/objects.htm#om40_objects

http://home.netscape.com/eng/mozilla/3.0/handbook/javascript/navobj.htm

http://www.w3.org/Press/DOM-core.html

Working with Elements on the Web Page

Dynamic HTML is about interacting with the Web page; thus, you must be able to gain access to the elements on the page (note that Internet Explorer is currently the only browser that exposes every page element).

The most explicit method to access an element is to use the all object, which you can index using an ordinal number, element name, or element ID. For example, the following JavaScript line sets e to the object representation of the element whose ID is Example. You can just as easily use the element's name or index it by its position in the all collection.

e = document.all( "Example" );

The document object model provides two alternatives for gaining access to a page element. Either of the following forms works. Note that you can simply write the ID of the element as the object, shown in the second example, as long as the ID is unique across the entire Web page and does not conflict with names found in the object model or JavaScript.

e = document.all.Example
e = Example


NOTE: Collections (Microsoft speak) and arrays (Netscape speak) provide access to a group of related items. For example, the forms collection is an array that contains a single element for each form on the Web page. Because forms is a child object of document, you access it like this: document.forms. You can index the forms collection by number, or you can use an element's ID or name, like this: document.forms("MyForm"). After you've created a reference to an object using a collection, you can access any of that object's properties, methods, events, or collections.

Accessing Multiple Elements in a Collection

In the previous examples, you assigned to e the element with the ID Example. If the Web page contains only one element with that ID, you can access any of its properties like this: e.property. What happens if the Web page contains more than one element with that ID, though? In that case, e is a collection, with each item in the collection pointing to one of the matching elements.

As shown in Listing 19.1, you can test the variable to see whether it contains a single value or is a collection. If the variable's length property is null, the variable contains a single value. If it's not null, the variable contains an array of values that you index using an ordinal number. You can visit each value in the collection, shown in the for loop, and access each value's properties.

Listing 19.1  Working with Collections

<HTML>
  <P ID=Test>Hello</P>
  <P ID=Test>Goodbye</P>
  <SCRIPT LANGUAGE=JAVASCRIPT>
    var i, e;
    e = document.all( "Test" );
    if( e.length == null )
      alert( "There is one element in the collection" );
    else
      for( i = 0; i < e.length; i++ )
        alert( i.toString() + ": " + e[i].innerText );
  </SCRIPT>
</HTML>

Changing the Content of an Element On-the-Fly

You probably already realize that you can dynamically write HTML to the Web page as the browser opens it. You do so using the document.write method. The problem is that you can't use this method to change the Web page's content after the browser finishes opening it. In Internet Explorer's object model, every element contains two interesting properties that allow you to do this (this doesn't work with Communicator):

Listing 19.2 shows an example that modifies the text you see on the Web page after it's open. At the top of the file, you see a <P> tag with the ID Test. When the user clicks the button, the browser calls the event handler ChangeText(). The first line of ChangeText() sets innerText to a value, which causes the browser to completely replace the text in the <P> tag with the new value. The third line sets innerHTML to a value that contains raw HTML, which the browser neatly inserts into the <P> container. Using both innerText and innerHTML, you can change the Web page's content without making a round-trip to the server.

Listing 19.2  Dynamically Changing the Web Page's Content

<HTML>
  <P ID=Test>Example Text</P>
  <FORM>
    <INPUT ID=Click TYPE=BUTTON VALUE="Click" onClick="ChangeText();">
  </FORM>
  <SCRIPT LANGUAGE=JAVASCRIPT>
    function ChangeText()
    {
      Test.innerText = "Dynamically Built Text";
      window.alert( "See the new text" );
      Test.innerHTML = "Dynamically Built <STRONG>HTML</STRONG>";
      window.alert( "See the new HTML" );
    }
  </SCRIPT>
</HTML>

Handling Events Generated by the Object Model

You, as a JavaScript programmer, are already familiar with creating event handlers. There are a few things you should know about Internet Explorer's and Communicator's event models, however, which relate specifically to events generated by the document object model:

Listing 19.3  Event Bubbling versus Event Capturing

<HTML>
  <BODY>
    <SCRIPT LANGUAGE=JAVASCRIPT>
      function Hello()
      {
        window.alert( "You clicked the heading" );
      }
    </SCRIPT>
    <P>Click <A onClick="Hello();" HREF="">here</A>, and you see a message</P>
  <BODY>
</HTML>

These two differences are extremely important to you if you're building cross-platform Web pages. Event bubbling and event capturing are techniques that you can use to write a single event handler to handle events for a group of objects. It's a great convenience. You learn about this topic in the following sections.


CAUTION: If you're writing cross-platform Web pages, don't rely on event bubbling or capturing at all. Each method is incompatible with the other, so your page won't work in both browsers.

Listing 19.4  Understanding Event Bubbling

<HTML>
  <BODY onclick="alert( `Click!' );">
  <DIV onClick="alert(`Outside');">
    <DIV onClick="alert(`Middle');">
      <SPAN onClick="alert(`Inside');">Click Me</SPAN>
    </DIV>
  </DIV>
  </BODY>
</HTML>

Getting More Information About an Event

Both Internet Explorer and Communicator provide additional information to your event handlers in the form of the event object. This object is only available within an event handler. event is implemented differently in Internet Explorer and Communicator. Table 19.1 shows some of the most useful properties for the event object in both browsers.

Table 19.1  The event Object

Property Description
Internet Explorer
cancelBubble Set true to cancel the bubble
keycode ASCII keycode of the key that was pressed
returnValue Set false to cancel action (that is, form post)
srcElement HTML object that caused the event
x Mouse x position when event was raised
y Mouse y position when event was raised
Netscape Communicator 4
target HTML object that caused the event
type The type of event that was raised
pageX Mouse x position when the event was raised
pageY Mouse y position when the event was raised
which Mouse button number or ASCII keycode

Canceling an Event's Bubble in Internet Explorer

You can't always let an event bubble all the way to the top of the page's hierarchy. For example, if you have a handler for onClick in both <BODY> and <P> tags, Internet Explorer is going to invoke both of them--probably not what you intended.

In Listing 19.5, for example, when the user clicks Click Here, the event first goes to the <P> tag, which displays the message You clicked the paragraph. Then the event automatically bubbles up to the <BODY> tag, which displays the message You clicked elsewhere in the body. To prevent the event from bubbling up past a certain point, set event.cancelBubble to true in your event handler. In Listing 19.5, simply uncomment the last line in the script. This prevents the browser from sending the event any farther up the document's hierarchy.

Listing 19.5  Canceling an Event's Bubble

<HTML>
  <BODY onClick="alert(`You clicked elsewhere in the body');">
  <P onClick="ClickMessage();">Click Here</P>
  <SCRIPT LANGUAGE=JAVASCRIPT>
    function ClickMessage()
    {
      alert( "You clicked the paragraph" );
      // window.event.cancelBubble = true;
    }
  </SCRIPT>
</BODY>
</HTML>

Handling an Event for a Group of Objects

As noted earlier, event bubbling makes it possible to handle an event for a group of related objects. For example, if you want to create a rollover effect for five blocks of text, you can write an event handler for each block, or you can close all five blocks within a <DIV> tag and write a single event handler for the <DIV> tag. Remember that the event object tells you exactly which object generated the event using the srcElement property. You test this property to determine which object in a group caused the event; then you handle it appropriately.

Listing 19.6 shows an example that uses a single event handler to handle the onClick event for a group of related objects. Note that all the <SPAN> tags have unique IDs and are enclosed within a single <DIV> tag. The <DIV> tag associates the function called LinkClick() with the onClick event. Within LinkClick(), you see an If statement that tests srcElement.id to match the appropriate code to the appropriate object. If the source element's ID is 1, the event handler displays You clicked the first item; if the ID is 2, the event handler displays You clicked the second item, and so on.

Listing 19.6  Handling Multiple Objects with a Single Event Handler

<HTML>
  <BODY onclick="alert( `Click!' );">
    <SCRIPT LANGUAGE=JAVASCRIPT>
      function LinkClick()
      {
        if( window.event.srcElement.id == "1" )
        {
          window.alert( "You clicked the first item" );
          window.event.cancelBubble = true;
        }
        else
          if( window.event.srcElement.id == "2" )
          {
            window.alert( "You clicked the second item" );
            window.event.cancelBubble = true;
          }
          else
            if( window.event.srcElement.id == "3" )
            {
              window.alert( "You clicked the third link" );
              window.event.cancelBubble = true;
            }
      }
    </SCRIPT>
    <DIV onClick="LinkClick();">
      <SPAN ID="1">First Item</SPAN><BR>
      <SPAN ID="2">Second Item</SPAN><BR>
      <SPAN ID="3">Third Item</SPAN>
    </DIV>
  </BODY>
</HTML>

Connecting Scripts to Styles and Classes

A growing part of dynamic HTML is the capability to change the style of an element within a script. Internet Explorer 4.0 is currently the only browser that provides this capability, but after W3C has a document object model recommendation, Communicator will quickly follow suit.

Internet Explorer's document object model exposes all the styles for every element on the Web page. You access an element's style using the style property, as shown in the following line of code. Test.style is the style object for the element called Test; color is the property that reflects the CSS color attribute.

Test.style.color = "red";

Appendix B, "Style Sheet Property Reference," shows you all the possible CSS attributes. The document object model exposes all of them. You must know a few rules about naming CSS attributes in your scripts, though, because CSS uses dashes in attribute names, and that's not valid in JavaScript:

Listing 19.7 shows an example of a Web page that changes a <P> tag's background and foreground colors on-the-fly. The user types the proper name of the background and foreground and then clicks Change. The event handler ChangeColor() sets the backgroundColor and color properties for the Test element's style to the values supplied by the user.

Listing 19.7  Changing an Element's Style

<HTML>
  <P ID="Test">This is a regular HTML element</P>
  <HR>
  <FORM>
    Type a color name for the background:
    <INPUT ID=Background TYPE=TEXT SIZE=20 VALUE="White"><BR>
    Type a color name for the foreground:
    <INPUT ID=Color TYPE=TEXT SIZE=20 VALUE="Black">
    <INPUT TYPE=BUTTON VALUE="Change" onClick="ChangeColor( Background.value, Color.value );">
  <FORM>
  <SCRIPT LANGUAGE=JAVASCRIPT>
    function ChangeColor( Background, Color )
    {
      Test.style.backgroundColor = Background;
      Test.style.color = Color;
    }
  </SCRIPT>
</HTML>

Changing an element's individual style properties isn't always the most efficient way to change how an element looks. If you're changing a number of properties during an event handler, define two different classes for the element; then swap the classes during the event handler using the element's className property. Listing 19.8 shows such an example. Each time the user clicks Change, the browser calls the event handler ChangeColor(). ChangeColor() tests the Test element's className attribute to see which style is assigned to it. If it's currently set to Normal, the event handler changes it to Reverse, and vice versa. Note that the <P> tag initially sets the CLASS attribute to Normal.

Listing 19.8  Changing an Element's Class

<HTML>
  <STYLE>
    .Normal {background-color: white; color: black}
    .Reverse {background-color: black; color: white}
  </STYLE>
  <P ID="Test" CLASS="Normal">This is a regular HTML element</P>
  <HR>
  <FORM>
    Click button to change styles:
    <INPUT TYPE=BUTTON VALUE="Change" onClick="ChangeColor();">
  <FORM>
  <SCRIPT LANGUAGE=JAVASCRIPT>
    function ChangeColor()
    {
      if( Test.className == "Normal" )
        Test.className = "Reverse";
      else
        Test.className = "Normal";
    }
  </SCRIPT>
</HTML>

Redefining How Links Behave

With events and now styles under your belt, you are ready to put together a more complete example. The one shown in Listing 19.9 changes how links look and work on the Web page. When the user moves his mouse over an anchor, the anchor changes color. When he moves his mouse off the anchor, it changes back to its original color. Here's more information about this example:

Listing 19.9  Redefining How Links Behave

<HTML>
  <BODY onmouseover="RollOn();" onmouseout="RollOff();">
<STYLE>
    A { color: blue; font-size: smaller; font-family: arial;
        font-weight: bold; text-decoration: none }
    A.LitUp  { color: red; font-size: smaller; font-family: arial;
               font-weight: bold; text-decoration: underline }
  </STYLE>
  <SCRIPT LANGUAGE=JAVASCRIPT>
    function RollOn()
    {
      if( window.event.srcElement.tagName != "A" ) return;
      if( window.event.srcElement.className == "" )
        window.event.srcElement.className = "LitUp";
    }
    function RollOff()
    {
      if( window.event.srcElement.className == "LitUp" )
        window.event.srcElement.className = "";
    }
  </SCRIPT>
  Visit <A HREF="http://rampages.onramp.net/~jerry">Jerry Honeycutt's</A>     		  homepage.
  Alternatively, you can visit some of my favorite Web sites:
  <UL>
    <LI><A HREF="http://www.microsoft.com">Microsoft Corporation</A>
    <LI><A HREF="http://www.netscape.com">Netscape Corproation</A>
  </UL>
  </BODY>
</HTML>

Creating Rollover Effects for Toolbars and Menus

Listing 19.10 is to Listing 19.9 (it only works with Internet Explorer 4.0). You use it to create menus or toolbars that change as the user moves her mouse over them. Here's how it works:

Listing 19.10  Creating Rollover Effects for Toolbars and Menus

<HTML>
  <BODY>
  <SCRIPT LANGUAGE=JAVASCRIPT>
    function RollOn()
    {
      if( window.event.srcElement.className == "Normal" )
        window.event.srcElement.className = "LitUp";
    }
    function RollOff()
    {
      if( window.event.srcElement.className == "LitUp" )
        window.event.srcElement.className = "Normal";
    }
  </SCRIPT>
  <STYLE>
    .Normal { cursor: hand }
    .LitUp  { cursor: hand; color: white; background-color: black }
  </STYLE>
  <DIV onMouseOut="RollOff();" onMouseOver="RollOn();">
    <SPAN CLASS="Normal">First Item </SPAN>
    <SPAN CLASS="Normal">Second Item</SPAN>
    <SPAN CLASS="Normal">Third Item </SPAN>
  </DIV>
  </BODY>
</HTML>

FIG. 19.1
When the user moves the mouse pointer over one of the items in the <DIV> tag, the item changes colors.

Controlling an Object's Visibility

You can make certain elements on the Web page visible or invisible using the style object's display property. Set this property to none, and the browser hides the object, like this:

element.style.display = "none";

Set this property to an empty string, as shown in the following line of code, and the browser shows the element.


element.style.display = "";

Hiding an element also causes the browser to reflow the Web page so that there isn't a huge hole where the invisible element exists. Figure 19.2 shows a page before and after the item was hidden. FIG. 19.2 When the element is hidden, the browser reflows the Web page as if the element isn't there at all.

Showing and Hiding Portions of a Form

You can hide the advanced fields of a form from a beginning user and let an advanced user get to them by clicking a special button. The latter is the approach that Windows 95 and Windows NT 4.0 takes in many cases. Have you ever seen a dialog box in Windows with a button labeled Advanced or More? When the user clicks one of these buttons, the dialog box unfolds to show more fields.

Listing 19.11 is an example of such a form on a Web page. The last part of the form defined in this example contains a <DIV> tag with the ID More. It contains a number of fields that aren't displayed initially because the <DIV> tag is hidden. The onClick event handler for the FEEDBACK_MORE button you see in the form is OpenMore(). This function toggles a Boolean flag called MoreIsUp, which tracks the status of the hidden area in the form, and sets the <DIV> tag's display property accordingly. Note that it also changes the label on the button to reflect the status of MoreIsUp.

Listing 19.11  Showing and Hiding Portions of the Web Page

<HTML>
<SCRIPT LANGUAGE=JAVASCRIPT>
MoreIsUp = false;
// Display the hidden form if it's not already displayed. Otherwise, hide it.
// Also, change the text in the button to reflect the current state of the hidden form.
function OpenMore()
{
  MoreIsUp = !MoreIsUp;
  More.style.display = MoreIsUp ? "" : "none";
  FEEDBACK.FEEDBACK_MORE.value = MoreIsUp ? "Less<<" : "More>>";
}
</SCRIPT>
</HEAD>
<BODY>
<FORM NAME=FEEDBACK METHOD=POST ACTION="mailto:jerry@honeycutt.com">
  <TABLE CELLPADDING=10>
   <TR>
      <TD VALIGN=TOP>
         <B>Please provide your e-mail address:</B><BR>
         <INPUT NAME=FEEDBACK_MAIL TYPE=TEXT SIZE=40>
      </TD>
      <TD VALIGN=TOP>
        <B>How did you find our site:</B><BR>
        <SELECT NAME=FEEDBACK_HOW SIZE=1>
           <OPTION VALUE=1>AltaVista
           <OPTION VALUE=2>Excite
           <OPTION VALUE=3>Lycos
           <OPTION VALUE=4>Yahoo!
           <OPTION VALUE=5>WebCrawler
           <OPTION VALUE=6>Friend
           <OPTION VALUE=7>Other Link
      </SELECT>
      </TD>
    <TR>
      <TD VALIGN=TOP ROWSPAN=2>
        <B>Tell us what you think about our Web site:</B><BR>
        <TEXTAREA NAME=FEEDBACK_MEMO COLS=45 ROWS=8>
        </TEXTAREA>
      </TD>
      <TD VALIGN=TOP>
        <B>How did we rate?</B><BR>
        <TABLE BORDER=1>
          <TR ALIGN=CENTER>
            <TH></TH><TH>Yes</TH><TH>No</TH>
          </TR>
          <TR ALIGN=CENTER>
            <TD ALIGN=LEFT>
              Did this site load fast enough?
            </TD>
            <TD>
              <INPUT NAME=FEEDBACK_SPEED TYPE=RADIO>
            </TD>
            <TD>
              <INPUT NAME=FEEDBACK_SPEED TYPE=RADIO>
            </TD>
          </TR>
          <TR ALIGN=CENTER>
            <TD ALIGN=LEFT>
              Did you find the graphics interesting?
            </TD>
            <TD>
              <INPUT NAME=FEEDBACK_GRAPHIC TYPE=RADIO>
            </TD>
            <TD>
              <INPUT NAME=FEEDBACK_GRAPHIC TYPE=RADIO>
            </TD>
          </TR>
          <TR ALIGN=CENTER>
            <TD ALIGN=LEFT>
                Was the content suitable?
            </TD>
            <TD>
              <INPUT NAME=FEEDBACK_CONTENT TYPE=RADIO>
            </TD>
            <TD>
              <INPUT NAME=FEEDBACK_CONTENT TYPE=RADIO>
            </TD>
          </TR>
         </TABLE>
      </TD>
    </TR>
    <TR ALIGN=RIGHT>
      <TD>
         <TABLE WIDTH=100%>
           <TD ALIGN=LEFT>
             <INPUT NAME=FEEDBACK_MORE TYPE=BUTTON VALUE="More>>"      OnClick="OpenMore()">
           </TD>
           <TD>
             <INPUT NAME=FEEDBACK_RESET TYPE=RESET VALUE=Clear>
             <INPUT NAME=FEEDBACK_SUBMIT TYPE=SUBMIT VALUE=Submit>
           </TD>
         </TABLE>
      </TD>
     </TR>
  </TABLE>
  <!-- This division contains the hidden part of the form that the user sees when they click on More>>. The event handler at the top of this file shows the 	  layer. -->
  <DIV ID=More STYLE="display: none">
    <TABLE CELLPADDING=10>
      <TR>
        <TD>
          <B>Type the URL of your home page:</B><BR>
          <INPUT NAME=FEEDBACK_URL TYPE=TEXT SIZE=60>
        </TD>
        <TD>
          <B>Type your phone number:</B><BR>
          <INPUT NAME=FEEDBACK_PHONE TYPE=TEXT SIZE=32>
        </TD>
      </TR>
    </TABLE>
  </DIV>
</FORM>
</BODY>
</HTML>

Creating an Expanding/Collapsible Outline

Listing 19.12 uses the display property to build a collapsible outline. Initially, the top-level topics are showing. If the user clicks a topic heading that is collapsed, the browser expands the next level of the outline under that topic. If the user clicks a topic heading that is expanded, the browser collapses all the content under it.

You can build your outline using any HTML tags you want, as long as you nest tags for subtopics within the tags of parent topics. In this example, I used the <UL> tag because it automatically indents each outline level. The only thing you absolutely must do for the scripts to work is to give each tag an appropriate ID. The top level of the outline can have any ID you want; OL in this case. Each subtopic would then append a serial number to it indicating its position in the outline: 1, 2, 3, and so on. Any third-level topics would again append a serial number to the parent's ID. Here's what the IDs would look like for a simple outline that has one main topic, two under that, and three third-level topics:
Outline

Outline1

Outline11

Outline12

Outline13

Outline2

Outline21

Outline22

Outline23
To identify the elements that belong to the outline, you assign the Outline class to the CLASS attribute of each element. You must also set the display property of each subitem to none so that it doesn't show initially. Here's what a typical line in an outline might look like:

<P ID="Outline12" CLASS="Outline" STYLE="display: none">Outline Item</P>

The last thing you must do is surround the entire outline with a <DIV> tag. The tag must associate the OutlineClick() function with the onClick event. Because events bubble up from each outline element to the <DIV> tag, a single script is all that's required to handle every outline level.

The script contains three functions that make the outline work:

Listing 19.12  Creating an Expanding/Collapsible Outline

<HTML>
<SCRIPT LANGUAGE=JAVASCRIPT>
  function OutlineClick()
  {
    var Source, Targets, i;
    Source = window.event.srcElement;
    if( Source.className == "Outline" )
    {
      Targets = document.all(Source.id).children;
      if( Targets.length != 0 && Targets[0].style.display == "none")
        Expand( Source.id );
      else
        Collapse( Source.id );
      window.event.cancelBubble = true;
    }
  }
  function Expand( Level )
  {
    var i, Target;
    i = 1;
    Target = document.all( Level + i.toString() );
    while( Target != null )
    {
      Target.style.display = "";
      i++;
      Target = document.all( Level + i.toString() );
    }
  }
  function Collapse( Level )
  {
    var i, Target;
    if( document.all( Level ).children.length == 0 )
      return false;
    else
    {
      i = 1;
      Target = document.all( Level + i.toString() );
      while( Target != null )
      {
        Collapse( Target.id );
        Target.style.display = "none";
        i++;
        Target = document.all( Level + i.toString() );
      }
    }
  }
</SCRIPT>
<STYLE>
  .Outline { cursor: hand }
</STYLE>
<DIV onClick="OutlineClick();">
  <UL ID="OL" CLASS="Outline" >
    Outline (click to expand outline)
    <UL ID="OL1" CLASS="Outline" STYLE="display: none">
      First Outline Item
        <P ID="OL11" CLASS="Outline" STYLE="display: none">
        You can nest content as deep as you like within the outline.<BR>
        As long as you use the appropriate IDs and assign each outline<BR>
        element to the Outline class, the outline will work automatically.<BR>
        Just copy the scripts in this example to your HTML file.<BR>
        Note that you can use this outline script with any HTML tags.<BR>
        I've used the UL tag here, though, so that the outline would<BR>
        be automatically indented at each level.
        </P>
      <UL ID="OL12" CLASS="Outline" STYLE="display: none">
        A Sub-item underneath the first outline item
      </UL>
      <UL ID="OL13" CLASS="Outline" STYLE="display: none">
        Another sub-item underneath the first outline item
        <UL ID="OL131" CLASS="Outline" STYLE="display: none">Test 1</UL>
      </UL>
    </UL>
  </UL>
</DIV>
</HTML>

Positioning HTML Elements on the Web Page

Both Internet Explorer and Communicator use CSS positioning, which you learned about in Chapter 18, "Positioning HTML Elements." You can script the position of an element using the style object. You use the left, posLeft, top, posTop, and zIndex properties to affect the position of elements from within your scripts.

For example, use the following code to change an element's position to an absolute position of 30, 45:

element.style.left = 30;
element.style.top = 45;


TIP: Shun Netscape layers in favor of Cascading Style Sheet positioning. They work roughly the same and are compatible across both Internet Explorer and Communicator.

The examples in the following sections show you how to use positioning to create some astonishing effects for your Web page. Note that a recurring theme in these and most positioning examples is the timer. In the document object model, you can set a timer that raises an event at the end of the allotted time. The following line of code shows you how to set a timer. The first parameter to setTimeout() is the function call that the timer should make when the timer expires, Ding() in this case. The second parameter is the number of milliseconds for which to set the timer. Remember that there are 1,000 milliseconds in one second. The third parameter is the language used by the function listed in the first parameter.


Timer = window.setTimeout( "Ding()", 500, "JAVASCRIPT" );

Creating a Simple Animation

Listing 19.13 shows how to build simple animations using a combination of CSS positioning and the document object model's timer. The object that we're animating in this example is the <DIV> tag shown at the end of the listing. This tag has the ID Ani, and the position property is set to absolute.

At each timer event, the event handler Ding(), which is associated with the timer, moves the <DIV> tag just a bit farther. The variables Hdir and Vdir indicate the horizontal and vertical directions that the object moves. To calculate the horizontal offset, for example, Hdir is multiplied by 10, and the result added to the current left-hand position as an offset. The event handler has to set the timer again because the timer only goes off once after it's set.

This example has an additional feature. The function ChangeDirection() is associated with the document's onClick event. When the user clicks the desktop, ChangeDirection() checks the event object to see where the user clicked. ChangeDirection() changes Hdir and Vdir to indicate the direction relative to the object's current position on which the user clicked.

Listing 19.13  Creating a Simple Animation

<HTML>
<BODY onClick="ChangeDirection()">
  <SCRIPT LANGUAGE=JAVASCRIPT>
    var Timer;
    var Hdir = 1;
    var Vdir = 1;
    function Ding()
    {
      Ani.style.posTop += Vdir * 10;
      Ani.style.posLeft += Hdir * 10;
      Timer = window.setTimeout( "Ding()", 500, "JAVASCRIPT" );
    }
    Timer = window.setTimeout( "Ding()", 500, "JAVASCRIPT" );
    function ChangeDirection()
    {
      if( window.event.clientX < Ani.style.posLeft )
        Hdir = -1;
      else
        Hdir = 1;
      if( window.event.clientY < Ani.style.posTop )
        Vdir = -1;
      else
        Vdir = 1;;
    }
  </SCRIPT>
  <DIV ID=Ani STYLE="position: absolute; left: 0; top: 0">
    This content is going to move across the screen.
  </DIV>
</BODY>
</HTML>


TIP: You can create much more advanced animations than the one shown in this example. To do so, fill an array with a "storyboard" that indicates the position and time at that position. Then the Ding() function can set the position and time according to the storyboard. This gives you complete control over how an object moves across the screen.

Implementing Fly-Over Help for Your Links

You can give the user ToolTip-style help for the links on your Web page (or any element on your Web page actually). Here's how it works:

Listing 19.14 implements these features as shown in Figure 19.3. The only style defined in this example is Help, which describes how the help window looks. In this case, it has a yellow background and black text. It also puts a frame around the item. The <DIV> tag you see at the top of the listing is an absolutely positioned element that is used as the help window. It doesn't cause the Web page to reflow, so it looks more like a pop-up window.

Each link on the Web page defines the onMouseOver and onMouseOut events. The onMouseOver event is associated with the FlyOver() function, which accepts as its only parameter the text to display in the help window. The onMouseOut event is associated with the ClearFlyOver() function, which hides the help window. Here's how these functions work together to display the fly-over help:

Listing 19.14  Implementing Fly-Over Help for Your Links

<HTML>
  <STYLE>
    .Help {
            position: absolute; posTop: 0; posLeft: 0;
            border-width: thin; border-style: solid;
            background-color: yellow; color: black;
            height=20; width=240;
          }
  </STYLE>
  <DIV ID=FOArea CLASS="Help" STYLE="display: none">
  </DIV>
  <SCRIPT LANGUAGE=JAVASCRIPT>
    var HelpX, HelpY, HelpText;
    var ToShow, ToClear;
    function DoFlyOver()
    {
      if( ToClear != -1 ) window.clearTimeout( ToClear );
      FOArea.innerText = HelpText;
      FOArea.style.posLeft = HelpX + 8;
      FOArea.style.posTop = HelpY + 8;
      FOArea.style.display = "";
      ToClear = setTimeout( "ClearFlyOver()", 10000, "JAVASCRIPT" );
    }
    function ClearFlyOver()
    {
      FOArea.style.display = "none";
    }
    function FlyOver( Text )
    {
      HelpText = Text;
      HelpX = window.event.clientX;
      HelpY = window.event.clientY;
      ToShow = setTimeout( "DoFlyOver()", 1000, "JAVASCRIPT" );
    }
  </SCRIPT>
  <A onMouseOut="ClearFlyOver();"
     onMouseOver="FlyOver( `Open Jerrys home page' );"
     HREF="http://rampages.onramp.net/~jerry">Jerry Honeycutt</A><BR>
  <A onMouseOut="ClearFlyOver();"
     onMouseOver="FlyOver( `Open Microsoft Web Page' );"
     HREF="http://www.microsoft.com">Microsoft</A>
</HTML>

FIG. 19.3
You can associate fly-over help with any HTML tag that you put on your Web page.

Reading and Writing Cookies Using the Object Model

Most applications you run on your desktop remember your last settings and recall those settings each time you open the appropriate dialog box. Unfortunately, HTML forms don't follow this behavior. Some browsers will remember your settings during the current session; however, when you close and reopen the browser, those settings are lost.

You can emulate this behavior using cookies. You use cookies to save information on the user's computer that you can recall and use during a later session (this causes some users great consternation). The browser's built-in objects don't provide all the functions you need to use cookies, though. You need to define functions to save and parse cookies. The script shown in Listing 19.15 contains all the functions you need to use cookies in your own HTML documents:

Day,DD-MMM-YYYY HH:MM:SS GMT

The remaining two functions in this script, WriteCookies() and GetCookies(), are specific to the form in listing 19.15. WriteCookies() writes a value to the cookie for each field in the form. GetCookies() reads all the values from the document's cookie and sets each field in the form appropriately.

You're now armed with the functions to save the form's values to the document's cookie. Now you need to hook them up. The perfect place to save the values to the cookie is in the form's validation function: IsValid(). Add the following two lines of HTML to the end of this function (just before the last return statement); then, when the user submits a valid form, the validation function will save the form's values to the cookie.

if( blnValid )
    WriteCookies();

How about prefilling the form with the values in the cookie? That's easy. Change the <BODY> tag so that it looks like the following line. This associates the window's OnLoad event with the GetCookies() function, which prefills the form with the values in the cookie each time the user loads the HTML document containing the form.

<BODY OnLoad="GetCookies()">

Listing 19.15  Reading and Writing Cookies Using the Object Model

<HTML>
<SCRIPT LANGUAGE=JAVASCRIPT>
MoreIsUp = false;
// Display the hidden form if it's not already displayed. Otherwise, hide it.
// Also, change the text in the button to reflect the current state of the hidden form.
function OpenMore()
{
  MoreIsUp = !MoreIsUp;
  More.style.display = MoreIsUp ? "" : "none";
  FEEDBACK.FEEDBACK_MORE.value = MoreIsUp ? "Less<<" : "More>>";
}
</SCRIPT>
<SCRIPT LANGUAGE=JAVASCRIPT>
// Extract the value from the cookie at the given offset.
function GetValue( Offset )
{
  var End = document.cookie.indexOf (";", Offset);
  if( End == -1 )
    End = document.cookie.length;
  // Return the portion of the cookie beginning with the offset
  // and ending with the ";".
  return unescape( document.cookie.substring( Offset, End) );
}
// Return the value of a cookie by name.
function GetCookie( Name )
{
  var Len = Name.length;
  // Look at each substring that's the same length as the cookie name
  // for a match. If found, look up the value and return it.
  var i = 0;
  while( i < document.cookie.length )
  {
    var j = i + Len + 1;
    if( document.cookie.substring( i, j) == (Name + "=") )
      return GetValue( j );
    i = document.cookie.indexOf( " ", i ) + 1;
    if( i == 0 )
      break;
  }
  return null;
}
// Create or change a cookie given it's name and value. The name and value
// are required, but the expiration date isn't. Note that if you don't specify
// an expiration date, the cookie only exists for the current session.
function SetCookie( Name, Value, Expire )
{
  document.cookie = Name + "=" + escape( Value ) + ";expires=" + Expire;
}
// Write all the cookies for the FEEDBACK form.
function WriteCookies()
{
  var Expire = "Friday,25-Feb-2000 12:00:00 GMT";
  with( document.FEEDBACK )
  {
    SetCookie( "Mail", FEEDBACK_MAIL.value, Expire );
    SetCookie( "How", FEEDBACK_HOW.selectedIndex, Expire );
    SetCookie( "Memo", FEEDBACK_MEMO.value, Expire );
    SetCookie( "Speed", FEEDBACK_SPEED[0].checked ? "1" : "0", Expire );
    SetCookie( "Content", FEEDBACK_CONTENT[0].checked ? "1" : "0", Expire );
    SetCookie( "Graphic", FEEDBACK_GRAPHIC[0].checked ? "1" : "0", Expire );
  }
}
// Load the form with the values in the cookie.
function GetCookies()
{
  with( document.FEEDBACK )
  {
    FEEDBACK_MAIL.value = GetCookie( "Mail" );
    FEEDBACK_HOW.selectedIndex = GetCookie( "How" );
    FEEDBACK_MEMO.value = GetCookie( "Memo" );
    FEEDBACK_SPEED[0].checked = (GetCookie( "Speed" ) == "1");
    FEEDBACK_SPEED[1].checked = (GetCookie( "Speed" ) == "0");
    FEEDBACK_CONTENT[0].checked = (GetCookie( "Content" ) == "1");
    FEEDBACK_CONTENT[1].checked = (GetCookie( "Content" ) == "0");
    FEEDBACK_GRAPHIC[0].checked = (GetCookie( "Graphic" ) == "1");
    FEEDBACK_GRAPHIC[1].checked = (GetCookie( "Graphic" ) == "0");
  }
}
function IsValid()
{
  blnValid = true;
  with( document.FEEDBACK )
  {
    if( FEEDBACK_MAIL.value == "" )
    {
      window.alert( "You must provide a mail address" );
      blnValid = false;
    }
    if( !(FEEDBACK_SPEED[0].checked || FEEDBACK_SPEED[1].checked) ||
        !(FEEDBACK_CONTENT[0].checked || FEEDBACK_CONTENT[1].checked) ||
        !(FEEDBACK_GRAPHIC[0].checked || FEEDBACK_GRAPHIC[1].checked))
    {
      window.alert( "Please select Yes or No for each rating" );
      blnValid = false;
    }
  }
  if( blnValid )
    WriteCookies();
  return blnValid;
}
</SCRIPT>
<BODY onLoad="GetCookies();">
<FORM NAME=FEEDBACK METHOD=POST ACTION="" onSubmit="return IsValid();">
  <TABLE CELLPADDING=10>
   <TR>
      <TD VALIGN=TOP>
         <B>Please provide your e-mail address:</B><BR>
         <INPUT NAME=FEEDBACK_MAIL TYPE=TEXT SIZE=40>
      </TD>
      <TD VALIGN=TOP>
        <B>How did you find our site?:</B><BR>
<SELECT NAME=FEEDBACK_HOW SIZE=1>
           <OPTION VALUE=1>AltaVista
           <OPTION VALUE=2>Excite
           <OPTION VALUE=3>Lycos
           <OPTION VALUE=4>Yahoo!
           <OPTION VALUE=5>WebCrawler
           <OPTION VALUE=6>Friend
           <OPTION VALUE=7>Other Link
      </SELECT>
      </TD>
    <TR>
      <TD VALIGN=TOP ROWSPAN=2>
        <B>Tell us what you think about our Web site:</B><BR>
        <TEXTAREA NAME=FEEDBACK_MEMO COLS=45 ROWS=8>
        </TEXTAREA>
      </TD>
      <TD VALIGN=TOP>
        <B>How did we rate?</B><BR>
        <TABLE BORDER=1>
          <TR ALIGN=CENTER>
            <TH></TH><TH>Yes</TH><TH>No</TH>
          </TR>
          <TR ALIGN=CENTER>
            <TD ALIGN=LEFT>
              Did this site load fast enough?
            </TD>
            <TD>
              <INPUT NAME=FEEDBACK_SPEED TYPE=RADIO>
            </TD>
            <TD>
              <INPUT NAME=FEEDBACK_SPEED TYPE=RADIO>
            </TD>
          </TR>
          <TR ALIGN=CENTER>
            <TD ALIGN=LEFT>
              Did you find the graphics interesting?
            </TD>
            <TD>
              <INPUT NAME=FEEDBACK_GRAPHIC TYPE=RADIO>
            </TD>
            <TD>
              <INPUT NAME=FEEDBACK_GRAPHIC TYPE=RADIO>
            </TD>
          </TR>
          <TR ALIGN=CENTER>
            <TD ALIGN=LEFT>
                Was the content suitable?
            </TD>
            <TD>
              <INPUT NAME=FEEDBACK_CONTENT TYPE=RADIO>
            </TD>
            <TD>
              <INPUT NAME=FEEDBACK_CONTENT TYPE=RADIO>
            </TD>
          </TR>
         </TABLE>
      </TD>
    </TR>
    <TR ALIGN=RIGHT>
      <TD>
         <TABLE WIDTH=100%>
           <TD ALIGN=LEFT>
             <INPUT NAME=FEEDBACK_MORE TYPE=BUTTON VALUE="More>>"      OnClick="OpenMore()">
           </TD>
           <TD>
             <INPUT NAME=FEEDBACK_RESET TYPE=RESET VALUE=Clear>
             <INPUT NAME=FEEDBACK_SUBMIT TYPE=SUBMIT VALUE=Submit>
           </TD>
         </TABLE>
      </TD>
     </TR>
  </TABLE>
  <!-- This division contains the hidden part of the form that the user sees     when they click on More>>. The event handler at the top of this file shows the     layer. -->
  <DIV ID=More STYLE="display: none">
    <TABLE CELLPADDING=10>
      <TR>
        <TD>
          <B>Type the URL of your home page:</B><BR>
          <INPUT NAME=FEEDBACK_URL TYPE=TEXT SIZE=60>
        </TD>
        <TD>
          <B>Type your phone number:</B><BR>
          <INPUT NAME=FEEDBACK_PHONE TYPE=TEXT SIZE=32>
        </TD>
      </TR>
    </TABLE>
  </DIV>
</FORM>
</BODY>
</HTML>


Differences Between Internet Explorer and Communicator
W3C is in the process of standardizing the document object model. Today, however, you have a problem. Both Internet Explorer and Communicator use different object models, making compatibility difficult. In fact, the differences in both object models is at the heart of what each vendor calls dynamic HTML:

Both browsers expose the traditional objects: window, navigator, document, history, location, anchor, applet, area, form, element, options, image, and link. Communicator exposes an additional object called layer, though, which allows you to programmatically work with proprietary layers. Internet Explorer exposes a variety of objects that are unique to it, including all, cell, rows, children, embeds, plugins, external, filters, rules, style, styleSheet, styleSheets, screen, scripts, TextRange, and userProfile.



Previous chapterNext chapterContents


Macmillan Computer Publishing USA

© Copyright, Macmillan Computer Publishing. All rights reserved.