DekGenius.com
Team LiB   Previous Section   Next Section

18.4 DHTML in Fourth-Generation Browsers

Internet Explorer 4 and Netscape 4 were the browsers that introduced DHTML technology to the Internet. Both browsers include partial support for the CSS1 standard and support the CSS positioning attributes (which were integrated into the CSS2 standard) that are critical to DHTML. Unfortunately, the DOM standard did not exist when these fourth-generation browsers were being developed, so they do not conform to that standard. Nevertheless, it is possible to achieve DHTML effects in both browsers.

18.4.1 DHTML in Internet Explorer 4

As we saw in Chapter 17, IE 4 does not support the document.getElementById( ) method, nor does it support an API for dynamically creating new nodes and inserting them into a document. Instead, it provides the document.all[] array as a way of locating arbitrary elements of the document and allows document content to be altered with the innerHTML property of document elements. IE 4 does not conform to the standards here, but it provides adequate alternatives.

Although traversing and modifying documents is an important part of DHTML, the focus of this chapter is on the dynamic use of CSS styles. The good news is that the DOM API described earlier for setting CSS style attributes through the style property was adopted from the IE 4 API. Thus, once you've used document.all[] to locate the document element you want to modify, you can script the styles of that element just as you would in a browser that fully supports the DOM API. (Remember, though, that IE 4 does not fully support CSS, so you should not expect all style properties to be scriptable.)

The CSS2 standard specifies that the position attribute can be used to specify absolute or relative positioning for any element in a document. IE 4 was implemented before CSS2 was complete, however, and it supports absolute positioning for only a certain subset of elements. Therefore, when using absolute positioning in IE 4, you should wrap the content you want to position or animate in <div> or <span> tags, which do honor the CSS position attribute.

18.4.2 DHTML in Netscape 4

Creating DHTML effects with Netscape 4 is a more complicated affair. Netscape 4 does not support a full object model, so it does not allow JavaScript programs to refer to arbitrary HTML elements. It cannot, therefore, allow access to the inline styles of arbitrary elements. Instead, it defines a special Layer object.[5] Any element that is absolutely positioned (that is, any element that has its position style set to absolute) is placed in a separate layer from the rest of the document. This layer can be independently positioned, hidden, shown, lowered below or raised above other layers, and so on. The Layer API was proposed to the W3C for standardization but was never standardized. For this reason, it has been dropped by the Mozilla project and is not supported in Mozilla or in Netscape 6. Thus, the techniques described in this section are useful only in the 4.x series of browsers from Netscape.

[5] Layers were introduced in Chapter 17, when we discussed Netscape 4 compatibility with the core DOM API. Here we expand that introduction and discuss how layers provide an alternative to the core DOM API and an alternative to the DOM API for accessing CSS styles.

Each independently positioned layer in a document is represented by a Layer object, and, not surprisingly, the layers[] array of the Document object contains the complete set of Layer objects in a document. (Layer objects appear in this array in the order in which they appear in the document.) Additionally, any layer that is given a name with the name or id attribute can be accessed by name. For example, if a layer specifies id="L2", you can refer to it in Netscape as document.L2 or as document.layers["L2"]. Although Netscape 4 does not provide a way to refer to arbitrary document elements, this layers[] array provides a way to refer to the most important dynamic elements.

A layer is something like a separate window or frame. Although the Layer object is not the same as the Window object, it does have a document property, just as windows and frames do. The document property of a Layer object refers to a Document object: each layer has its own totally independent HTML document. Layers can even be nested; we can output some HTML text into a nested layer with code like this:

document.layers[1].document.layers[0].document.write("Layers Are Fun!");
document.layers[1].document.layers[0].document.close(  ); 

Netscape 4 does not allow us to create or manipulate the nodes of the document tree, and it does not even support the innerHTML property of Internet Explorer. However, the fact that layers contain independent documents does provide a technique for dynamically modifying document content.

Although Netscape 4 defines a layer as an element with the CSS position style set, it does not define any way to script the styles of a layer element directly. Instead, the Layer object defines properties and methods that we can use to dynamically position layers.

The properties of the Layer object have names that are similar to important CSS style attributes, but these layer properties are not exactly the same as style properties. For example, the left and top properties of the Layer object specify the pixel position of the layer; setting these properties of a layer is like setting the left and top style properties of an element, except that the Layer properties expect numeric pixel values instead of strings that include a numeric value and a unit specification. The visibility property of a layer specifies whether the contents of the layer should be visible; it is a lot like the style property with the same name, except that it expects a value of show or hide instead of the CSS standard visible or hidden. The Layer object also supports a zIndex property that works just like the zIndex style property. Table 18-3 lists key CSS style properties and the Layer property that is most closely equivalent to each. Note that these are the only style properties that Netscape 4 allows to be scripted.

Table 18-3. Layer properties in Netscape 4

CSS property

Equivalent Layer property

Layer notes

left, top
left, top

Specify pixels without units. See also moveTo( ) and moveBy( ).

zIndex
zIndex

See also moveAbove( ), moveBelow( ).

visibility
visibility

Layer returns show or hide even if you set this property to the standard visible and hidden values.

clip
clip.bottom, clip.height, clip.left, clip.right, 
clip.top, clip.width

Specify pixels without units.

backgroundColor
bgColor
 
backgroundImage
background.src

Set to a URL string.

As you can see from Table 18-3, the Layer object supports a couple of useful properties that are not related to dynamic positioning. The background.src property specifies a background image for the layer, and the bgColor property specifies a background color for the layer. These properties correspond to the backgroundImage and backgroundColor style properties.

In addition to its properties, the Layer object offers a number of convenient methods. moveBy( ) and moveTo( ) move a layer by a relative amount or to an absolute position. moveAbove( ) and moveBelow( ) set the zIndex of a layer relative to that of some other layer. See the client-side reference section of this book for a complete list of Layer properties and methods.

Because every layer contains an independent document, you can dynamically update the contents of a layer with the open( ), write( ), and close( ) methods of the Document object, as we saw in Chapter 14. In addition, the src property of a layer specifies the URL of the document that it displays. By setting this property, you can force the browser to load an entirely new document for display in the layer. The load( ) method is similar; it loads a new URL and changes the layer's width at the same time. Because layers often contain dynamically generated content, you may find it convenient to use javascript: URLs with the src property and load( ) method.

We've seen that Netscape 4 automatically creates a Layer object for any element that has its position style property set to absolute. The Netscape 4 API also allows layers to be created in other, less standards-compliant ways. For example, Netscape 4 defines an HTML <layer> tag that allows layers to be defined directly in HTML. <layer> remains a proprietary Netscape 4 extension; it was not included in the HTML 4 standard and is not supported in Mozilla or Netscape 6. More importantly, though, Netscape 4 supports a Layer( ) constructor that allows Layer objects to be dynamically created, as needed within a program. See the client-side reference section of this book for details.

18.4.3 Example: A Cross-Platform DHTML Animation

Despite the differences between the DOM API, the IE 4 API, and the Netscape Layer API, it is still possible to create DHTML effects that work in DOM-compliant browsers, in pre-DOM versions of IE, and in Netscape 4. Example 18-6 shows one way it can be done. This script displays the word "Hello" and animates it in a straight line from one point in the browser window to another.

Note the compatibility technique used in this example: we test for the existence of key functions, arrays, and properties before using them. If the Document object has a property named getElementById, we assume that we have a DOM-compliant browser with that property referring to the getElementById( ) method. Similarly, if the Document object has a property named all, we assume that we're running in Internet Explorer and use the document.all[] array to locate the element to be animated.

Example 18-6. A cross-browser DHTML animation script
<!-- This is the dynamic element we will animate. We wrap the h1 tag in a -->
<!-- div because IE 4 won't move the h1 without a div or a span container. -->
<div id="title" style="position:absolute"><h1>Hello</h1></div>

<!-- This is the JavaScript code that performs the animation -->
<script>
// These variables set the parameters for our animation:
var id = "title";                  // Name of the element to animate
var numFrames = 30;                // How many frames to display
var interval = 100;                // How long to display each frame
var x0 = 100, y0 = 100;            // The element's starting position
var x1 = 500, y1 = 500;            // The element's ending position
var dx = (x1 - x0)/(numFrames-1);  // Distance to horizontally move each frame
var dy = (y1 - y0)/(numFrames-1);  // Distance to vertically move each frame
var frameNum = 0;                  // Frame we are at now
var element = null;                // The element to be animated

// First, we find the element to be animated. Use a DOM-compliant technique
// if the browser supports it; otherwise, fall back on browser-specific code.
if (document.getElementById) {              // If this is a DOM-compliant browser,
    element = document.getElementById(id);  // use the DOM method
}
else if (document.all) {            // Otherwise, if the IE API is supported,
    element = document.all[id];     // use the all[] array to find the element
}
else if (document.layers) {         // Else, if the Netscape API is supported,
    element = document.layers[id];  // use the layers[] array to get the element
}

// If we found the element to animate using one of the previous techniques,
// start animating it by calling nextFrame(  ) every interval milliseconds
if (element) {
    var intervalId = setInterval("nextFrame(  )", interval);
}

// This function is repeatedly called to display each frame of the animation.
// It moves the element using either the DOM API for setting CSS style
// properties or, if the browser does not support that API, the Netscape
// Layer API.
function nextFrame(  ) {
    if (element.style) {
        // If the browser supports it, move the element by setting CSS
        // style properties. Note the inclusion of the units string.
        element.style.left = x0 + dx*frameNum + "px";
        element.style.top = y0 + dy*frameNum + "px";
    }
    else {
        // Otherwise, assume that element is a layer, and move it by
        // setting its properties. We could also use element.moveTo(  ).
        element.left = x0 + dx*frameNum;
        element.top = y0 + dy*frameNum;
    }

    // Increment the frame number, and stop if we've reached the end
    if (++frameNum >= numFrames) clearInterval(intervalId);
}
</script>
    Team LiB   Previous Section   Next Section