Special Edition Using HTML 4

- 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:

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.Examplee = 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.

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):

innerText contains the text content of a container tag without the HTML.

innerHTML contains the complete content of a container tag including
the HTML.

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.

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:

Internet Explorer supports the concept called event bubbling, which means
that an event bubbles up through the page's hierarchy until it reaches the top. If
the user clicks the word here in Listing 19.3, the event goes to the <A>
tag first; then it bubbles up to the <P> tag. The last two objects
to see the event are the document and the window. Listing 19.4 is an example that
you can use with Internet Explorer to help you better understand bubbling; open this
file in Internet Explorer, and watch each message as it pops up.

Communicator supports the concept called event capturing, which is the
opposite of event bubbling in that the event starts at the top of the page's hierarchy
and filters its way down. If the user clicks the word here, the event goes
to the document and window objects first; then it goes to the <A>
tag. Note that Communicator skips the <P> tag because it doesn't expose
any events.

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

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.

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.

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:

Capitalize the first letter immediately to the right of each dash.

Remove each dash from the style name.

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.

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.

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:

This example defines two styles: A and A.LitUP. A
becomes the default style used for all <A> tags in the Web page. A.LitUp
is the style used to change how links look when the user rolls his mouse over the
anchor.

Two event handlers are defined for the <BODY> tag: RollOn(),
which handles the onMouseOver event, and RollOff(), which handles
the onMouseOut event. Because these event handlers are defined in the <BODY>
tag, they'll capture any onmouseover or onmouseout events for the
document that weren't canceled.

RollOn() checks the source of the onMouseOver event. If the
event is not generated from an <A> tag, it returns. Otherwise, it
changes the style class for the source element to A.LitUp. This has the
effect of changing the color of the anchor when the user moves his mouse over it.

RollOff() also checks the source of the onMouseOut event. If
the event was generated by an <A> tag, it changes the style class
for the source element to A. This returns the color to the original color
for the anchor.

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:

Two styles are defined for the rollover effect: Normal and LitUp.
The first just changes the cursor to a hand so that the user knows she is over something
that's clickable. The second reverses the color of the object.

Each item in the toolbar is enclosed within a <SPAN> tag. Each
tag has a unique ID. All the items in the toolbar are enclosed within a single <DIV>
tag that defines event handlers for onMouseOver and onMouseOut.
Events generated in the <SPAN> tags bubble up to the <DIV>
tag.

The event handlers RollOn() and RollOff() toggles the style
for the object that generated the event so that the object appears to reverse colors
when the user moves her mouse over it, as shown in Figure 19.1.

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.2When 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.

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:

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:

OutlineClick(). This function is the event handler for the <DIV>
tag that surrounds the entire outline. This function first makes sure that the object
raising the event is the Outline class assigned to it. Then it checks to
make sure that the topic has children using the children collection. If
the object does have children and the children are not currently displayed, it calls
Expand(), passing its own ID as a parameter, to show all the children.

Expand(). This function starts with a counter set to 1 and appends the
counter to the ID given as a parameter. If an element with that combined ID exists,
the function hides it. The function then increments the counter by 1 and repeats
the process until it doesn't find any more subitems for that topic.

Collapse(). This function is a bit more difficult to understand. Because
it must collapse an arbitrary number of outline levels, it's a recursive function.
It accepts the ID of the level to collapse as a parameter. If the level has no children,
it simply returns. If the level does have children, it hides each child. To make
sure that any items appearing below each child are also hidden, it recursively calls
itself with the ID of the child item.

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.

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:

When the user hovers the mouse pointer over a link for about a second, the help
window for that item opens next to the pointer.

The help window disappears immediately when the user moves the mouse pointer
off the link.

If the mouse pointer hovers over the link for longer than about ten seconds,
the help window disappears of its own accord.

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:

FlyOver(). This function sets a timer for one second that calls DoFlyOver()
when the timer expires. It also stashes the help text and mouse coordinates in global
variables because these values are not available outside the onMouseOver
event.

DoFlyOver(). This function sets the text in the help's <DIV>
tag, sets the position of the <DIV> tag to a small offset from the
mouse pointer, and displays it by setting display to an empty string. Last,
it sets a timer that calls ClearFlyOver() after about ten seconds.

ClearFlyOver(). This function simply hides the help window by setting
its display property to none.

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:

SetCookie(). This function saves a value to the document's cookie by
the given Name with the value contained in Value.
It sets the expiration date to the date contained in Expire. If you
don't provide an expiration date, the value lasts as long as the current session.
Note that the date must be formatted like this:

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

GetCookie(). This function looks for a value called Name
and returns its value. It looks at each value in the document's cookie until it finds
the right one. Then it uses GetValue() to parse out the value.

GetValue(). This function looks for a value called Name
and returns its value. It looks at each value in the document's cookie until it finds
the right one. Then it uses GetValue() to parse out the value.

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.

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:

Netscape. The document object model does not go as far as Microsoft's.
It exposes a limited number of elements and doesn't expose style sheet properties
at all. With Communicator, then, you can't change how elements look on the Web page
after it loads the page.