Part II

Position: Static

Though it hadn't yet been named, Dynamic HTML made its debut in the spring of 1996. Netscape's early betas of Communicator were on the whole disappointing, but they did introduce one very interesting new feature called layers. Layers were Netscape's answer to Microsoft's inline frames, but better. Where frames vied with other elements for space on the canvas, layers could float above and below one another, effectively adding a third dimension. And they didn't just float free: They could be precisely positioned anywhere on the page, and their properties - such as position, clipping area, and source - were exposed to scripting. In other words, they could be rewritten, resized, and repositioned at will, which is pretty close to Dynamic HTML.

For some reason layers never really caught on, despite their amazing potential. Perhaps developers had grown wary of investing too much time in proprietary features, no matter how cool, especially as Netscape was losing its lockhold on the browser market. In any case, even while Communicator was still in beta, Netscape agreed to stop pushing its nonstandard features and submit to a kind of binding arbitration at the W3C. Though it was too late to be consistently supported in each rival's 4.0 browser - in fact, it has yet to be finalized - a standard has emerged in the form of CSS-P (Positioning HTML Elements with Cascading Style Sheets).

Recent developments have been rapid. For example, it took Netscape only a matter of weeks to adapt their layering engine to the CSS-P model, which is available in release versions of Communicator. Microsoft likewise supports a version of CSS-P in Internet Explorer 4.0. However, Explorer's scripting syntax is different, which means that for the time being authors will suffer the familiar compatibility headaches. I'll explain how these new models work and how they differ, but first it's important to understand exactly what DHTML adds to the user's experience and therefore why developers should bother.

Strictly speaking, HTML has long supported some dynamic features. Even Netscape 1.1 could handle server push, a CGI mechanism that allowed images to be refreshed or replaced without changing the page. The most famous early push page was the Amazing Fish Cam, which still lives on Netscape's Web site (http://home.netscape.com/fishcam/fishcam. html), though it's been spiffed up with some cute DHTML. The Fish Cam was a clever proof of concept, but it hardly took the Web by storm. Server push isn't really capable of anything much more interesting than cams, though a few enterprising developers attempted some intriguing feats. The Burn:Cycle Web site, for example ("burncycle.com," InterActivity, Jan. '96), used the technique for animation effects, though viewers need pretty serious bandwidth to follow the illusion (www.burncycle.com).

In any case, the user's relation to the Web remained passive: Users could request whatever page they liked, but then all they could do was watch it download. Slightly more interactive were forms, the HTML interface for passing parameters to a CGI script or application. Though principally intended for online surveys and transactions, forms hinted at interesting creative and dynamic possibilities. A user could pass values to a graphics engine, a dialect filter, or some other data processor that would then spit out a customized result, usually a new Web page.

But once again, the dynamics were on the server side: Users could vary the input, but servers still handled the output. All this changed when Netscape released version 2.0 of its Navigator Web browser in late '95. Perhaps the single most important software release in Web history - only the original Mosaic competes - Navigator 2 was the first browser to support plug-ins, frames, multi-image (animated) GIFs, and - most significantly - JavaScript. Java itself would have to wait for Navigator 3, and the world is still waiting for a killer applet. Meanwhile, JavaScript, originally dubbed LiveScript before a marketing-induced name-change, has proved far more than Java's poor cousin. It has changed the landscape of Web programming.

Pages as Objects
Dynamic Web content comes in many forms, and all of them trace to Navigator 2's new client architecture, which has since been embraced (and in some ways surpassed) by Microsoft Internet Explorer. The simplest and most ubiquitous is the GIF animation, the poor person's dynamic content, basically server push without the server. GIFs have no interactive depth - all users can do is watch them - but they offer a bit of excitement without adding much overhead to either development or delivery.

Far richer, more interactive, and more genuinely dynamic are embedded plug-in objects. Shockwave games, RealPlayer Webcasts, QuickTimeVR panoramas, and the like have definitely made the Web a lot more fun. But users' fun is liable to be relative to the speed of their Internet connection. And if they don't have the requisite plug-in, they'll have no fun at all. Many people, impatient or weary with frequent and lengthy downloads, opt not to discover what they're missing. Microsoft's efforts to automate the process via ActiveX have helped, but not much. The field is still littered with plug-in casualties, and it's safe to say that the likeliest survivors will be those bundled with the two major browsers.

And in the end, plug-in objects are - like Java applets - just appendages to a Web page, rather than integral parts. They sit in a box, walled off from the context. They have no intelligence of the rest of the page, nor the page of them - except insofar as they're scriptable, which at this point few are. And to the degree that they're scriptable, their intelligence is dependent on JavaScript, which is to say, on Dynamic HTML.

DHTML is not the same thing as JavaScript nor scripting languages more generally, which include Microsoft's clones (JScript and VBScript) and a new standardized superset called ECMAScript. But DHTML wouldn't exist without the client-side scripting model introduced in Navigator 2. JavaScript 1.0 was, in retrospect, not all that capable - only its forms handling was very developed - but by introducing object-oriented concepts, it changed the way authors thought of Web pages.

And JavaScript has come a long way. With versions 1.1 and 1.2, Netscape exposed more objects to scripting and multiplied the methods and events. (We can thank the Image object - added to JavaScript 1.1 - for the overused technique of image swapping on mouseovers.) Microsoft has gone furthest of all with its latest Document Object Model (DOM), implemented in Explorer 4.0. In this model, absolutely every page element is some sort of object, and almost all object properties are readable and writeable - content can be changed, hidden, or moved at the scripter's will.

The two competitors' object models, alas, aren't fully compatible. And until Netscape supports Microsoft's DOM, or until both conform to one standard, it's hardly worth your time mastering discrepant features - unless you're lucky enough to be coding for one browser only. However, there's still enough overlap to enable interesting cross-browser programming, assuming you know where the pitfalls lie.

Thankfully, both implementations build on the same core syntax, which will be familiar to those who have worked with object-oriented programming. The browser and its windows are the highest-level objects, containing various properties, methods, and sub-objects, whose states are stored in memory. The object hierarchy is described through dot notation, generally in the form object.sub-object.property. For example, the background color of a page is a property (called bgcolor) of the document object, itself a property of the window object. We may get at or read this property with the statement:

window.document.bgcolor

or, since window usually may be assumed, just document.bgcolor.

You may also set or write the background color by assigning it a value using the equal sign. For example, to change the background to pure red, which has a hexadecimal value of ff0000, you would write this statement:

document.bgcolor = "#ff0000";

By setting the color this way, we've already done something dynamic. And the more times we do it, the more dynamic it becomes. Using JavaScript's built-in scheduling methods, we could change the color from red to blue and back as often as once every millisecond.

Say, for the sake of argument, you'd like to do just that, except only once every thousand milliseconds. The script, by the standards of DHTML, is simplicity itself. It involves only two functions: one to set the color to red, the other to set it to blue, each of which also contains a line to call the other after a second. This timing is achieved through the setTimeout() method, which takes two arguments:

setTimeout('script to execute', delay in milliseconds);

So if we have a function called changeToRed() and we wish to run it after a second, we would write:

setTimeout("changeToRed()",1000);

If you've followed me so far, you'll probably be able to read through this script, which achieves our task:

<SCRIPT Language="JavaScript">
<!--
function changeToRed() {
document.bgcolor = "#ff0000";
setTimeout("changeToBlue()",1000);
}
function changeToBlue()
{
document.bgcolor = "#0000ff";
setTimeout("changeToRed()",1000);
}
changeToRed();
// -->
</SCRIPT>

This script, which you would normally place in the <HEAD> of an HTML document, defines two functions. The first, changeToRed(), changes the background color to red and then waits a second before calling the next, changeToBlue(), which does what it says. Then changeToBlue() restarts the cycle by calling changeToRed() again after another second. We initiate the loop in the final line by explicitly calling the changeToRed() function, and the loop will continue until the user unloads the page.

This script is good for little save annoyance, but it illustrates the key concepts of element exposure (the readability and writeability of document properties) and script execution (calling and timing window methods). What turns these dynamic features into proper Dynamic HTML is their marriage to a positioning model, which has arrived in CSS-P.

An End for the Transparent GIF?
Cascading Style Sheets (CSS) were originally developed and proposed by the W3C in 1996-97, and they were partially implemented in Explorer 3. (Support is still incomplete in both major browsers.) CSS was intended to address authors' frustrations with the well-known limitations of HTML layout and to head off the development of yet more proprietary browser extensions to address these frustrations.

The W3C's basic idea is that HTML, which was designed for structural (semantic) markup, shouldn't and indeed can't be re-engineered into a page description language. CSS separates formatting from structure, so that the HTML can remain clear and backward compatible. No more need to use list tags just for text indentation; no more proprietary and structurally meaningless additions like the FONT tag. With such kludges out of the way, HTML can return to its intended purposes, while CSS handles the layout.

Indeed, CSS does the job much better, though it doesn't yet entirely replace HTML formatting. (Tables will forever be used for layout.) For example, adding leading (interline spacing) to text is difficult, indeed painful, with just HTML - you have to use loads of transparent GIFs, which clog up your code and retard scrolling. The same effect is breathtakingly simple in CSS:

<P Style="line-height:16px;">The lines of this paragraph will be spaced exactly 16 pixels apart, no matter how many lines there are, where the lines break, how narrow or wide the window is, and no matter how big the font is.</P>

<P Style="font-size:12px; line-height:16px;">This paragraph will not only be set in lines that are 16 pixels high, but also in type that's exactly 12 pixels tall. If you prefer, you can specify type in points, inches, or centimeters rather than in pixel units.</P>

Inline styles like these are already superior to the FONT tag, but they're still cumbersome and only hint at the power of CSS. Not just individual containers but entire documents and even Web sites can be styled through detachable style sheets, which may contain specs for whole classes of elements.

Say you wanted to apply the same style to all text in <P> containers. Rather than repeat the STYLE attribute for each tag, you could just include this style sheet in the head of your document:

<STYLE Type="text/css">

<!--

P { font: 12pt/16pt Verdana, Helvetica, sans-serif; }

-->

</STYLE>

Just as in Microsoft Word or QuarkXPress, you can specify global definitions that will then apply to all elements of the same type. If you later decide you'd like all the text to be colored blue and set in bold 10pt Courier on a 13pt line, you don't have to modify all your <P> tags; you only have to change one line in the style sheet:

P { font: bold 10pt/13pt Courier; color: blue; }

That's a style sheet, but why cascading? Because the style defined for the element <P> will, so far as makes sense, be inherited by all its children - that is, all the elements it contains. So, for example, in this line of code

<P>A paragraph with <EM>emphasized</EM> text and a <A HREF="link.html">hypertext link</A>.</P>

the text contained by <EM> and <A> will inherit the properties defined for <P>, except those they override. (By default, EM will italicize the text, while A will underline and color it.)

Besides defining styles for whole elements, you can also define particular style classes and a style for any particular container. Here's a more interesting style sheet:

BODY { margin-left: 24pt; margin-top: 24pt;

color: black; background-color: white;

background-image: url(bgimg.jpg); }

H1 { margin-left: -24pt; margin-bottom: 12pt;

font: bold 24pt Helvetica; color: red; }

P { font: 12pt/16pt Verdana, Helvetica, sans-serif; }

.green { color: #009900; }

#date { text-align: right; border: 1pt solid black;

padding: 4pt; }

In plain English, this style sheet says:

The whole contents (BODY) of the document will by default be indented 24 points from the left and 24 points from the top; the foreground (text) color is black, the background color is white, and bgimg.jpg is the background image.

The text inside an <H1> tag should be red - overriding the inherited value of black - and set in 24pt Helvetica bold; it should also be outdented 24 points, relative to the value inherited from BODY.

The contents of all <P> tags should be set in 12pt Verdana on a 16pt line; if Verdana is not available on the user's system, Helvetica should be used; and if Helvetica is not available, then the default sans-serif font for that system should be used.

Elements defined as belonging to the class green will be 60% green (specified in hex) rather than the default black.

The unique document object identified as date (using the attribute ID=date) will be aligned to the right, and its contents will be inset 4 points from all sides of its container, or box, which will be outlined with a solid black border 1 point thick.

If we save this style sheet in its own file, let's say a document called styles.css, we can then attach it to any Web page we like. Take the following simple HTML document, which imports the style sheet via the <LINK> tag:

<HTML>

<HEAD>

<TITLE>A Simple Document</TITLE>

<LINK Rel="stylesheet" Type="text/css"

HREF="styles.css">

</HEAD>

<BODY>

<H1>A big red 24pt headline</H1>

<P>A nondescript paragraph, whose style is shared with all other "P" elements on the page, excepting paragraphs whose default style is overridden.</P>

<P Class="green">This is such a paragraph, whose default foreground color attribute is overridden by that of the class "green," but whose remaining attributes are inherited.</P>

<P ID="date">November 21, 1997</P>

</BODY>

</HTML>

Shuffling the Canvas
Style sheets are a wonderful improvement on HTML's native formatting capabilities. Used well, they can enhance design and readability, while degrading gracefully when viewed through older browsers, which will simply ignore the styles and format according to the structural markup.

So far, so good; but the version of CSS we've been using - known as CSS level 1 and soon to be supplanted by CSS2 - is just as static as plain old HTML. At the moment, there's nothing terribly dynamic you can do with it, at least not in Navigator, though Microsoft has exposed styles to scripting along with all other document properties, as I'll explain in a minute. True, crossplatform DHTML is based on the CSS-P extension, described in a separate W3C spec and partially implemented by both major browsers. Once we take care of Microsoft's proprietary scripting, we'll focus on CSS-P for most of the rest of this article.

Once you've defined a style in Navigator, you're stuck with it, which is also true of many other document elements (including text, table properties, image dimensions, and so forth). Though Netscape's scripting language is inherently more powerful, Microsoft's object model is far more extensive. Using the CSS <SPAN> tag, you may apply a style to as much or as little content as you like; in Explorer, you can also add object handlers to modify that style:

<P>A paragraph which includes a <SPAN Style="color:green" onMouseOver="this.style.color=red">green phrase.</SPAN> When you pass your mouse over the green text, it changes to red.</P>

Whole CSS classes may also be redefined in response to events, and objects may be revealed, hidden, or rewritten at any time - Explorer will automatically reflow and redraw text as needed.

CSS-P properties, on the other hand, are dynamic in both browsers - albeit only in their 4.x versions. If you're authoring for the Internet at large, you should be very cautious with your DHTML, unless you only use it for trivial bells and whistles. In fact, you'll soon discover that the only sane approach is to separate your DHTML entirely from your plain HTML, serving each visitor the appropriate one. Since this means writing two entirely different versions of each page, you'll probably want to focus your dynamic code where it's really going to count.

Which brings us, finally, to CSS-P and true DHTML. As you might guess from its name, CSS-P adds positioning to CSS1. Static style sheets allow you to set the alignment, margins, and padding of elements. CSS-P goes further, allowing you to redefine the layout plane, position elements precisely on the canvas, and, in conjunction with a scripting language, to dynamically modify their position and clipping boundaries.

CSS-P comprises three types of positioning: static, relative, and absolute. Static positioning is effectively no positioning at all - it neither moves the positioned element from its natural place in the page flow nor establishes any new positioning context. It's what you get by default - that is, it's what you get with standard CSS1. So the following two style definitions are exactly the same:

#item { width: 400px; margin-left: 12px; }

#item { position: static; width: 400px;

margin-left: 12px; }

Relative positioning is more interesting. Like static positioning, it takes as its point of reference an element's normal place in the page flow. However, it also establishes a new (Cartesian) layout plane at that point of reference and allows you to position the enclosed content in that plane. Say, for example, we enclose some text in a <SPAN> container with relative positioning:

<P Style="font: 18px/24px Times;">The paragraph begins normally, flowing into its default container or "box" on the page. Then we add <SPAN Style="position: relative; top: -4px; color: green;">relatively-positioned content, which is raised four pixels with respect to its default position.</SPAN> The rest of this paragraph will now flow normally.</P>

The SPAN tag defines a new positioning plane, or zero point, at the word "relatively," and then positions the content of the SPAN with respect to that zero point - in this case, four pixels in a negative vertical direction. (Positive top values run down the plane, while positive left values run right.)

Absolute positioning is similar to relative, except that it pays no mind to where an element would otherwise fall on the page. Absolutely positioned elements are more like independent, floating blocks whose zero point may be set arbitrarily. Thus, you have complete control over where they appear. In the previous example, where the SPAN begins will depend on contextual factors (font size, window width, etc.). The only context for an absolutely positioned element is the layout plane of its parent
element: the BODY of the document, or, if it's contained in another
positioned block, then the zero point of that block.

To make the abstract more clear, let's look at another example:

<BODY BgColor="white" Text="black">

<P>A statically-positioned block of content, which flows normally onto the canvas.</P>

<DIV Style="position: absolute; top: 50px; left: 100px; width: 400px;">

A. This division, or DIV, begins 50 pixels down and 100 pixels left from the upper left-hand corner of the page, which is the zero point inherited from BODY.
<SPAN Style="position: relative; left: -50px; top: 12px; color: green;">

B. This span of content is positioned fifty pixels to the left and twelve pixels down from where it would normally appear in the division.</SPAN>
<SPAN Style="position: absolute; left: -50px; top: 125px; width: 150px; color: red;">

C. This span, meanwhile, is positioned 50 pixels left and 125 pixels down, not with respect to where it would appear inline, but with respect to the zero point of its parent container, which in this case is the DIV.</SPAN>
</DIV>

</BODY>

(Caveat scriptor: In some cases, Netscape trips on nested positioning.)

One final note on these examples: You'll notice that I've made use of both SPAN and DIV to enclose positioned content. While positioning may actually be applied to any container (P, A, OL, etc.), SPAN and DIV have already acquired a certain privilege for the purpose. SPAN, introduced along with CSS in Explorer 3, was specially devised for applying styles to inline content, which is to say, to portions of a larger block. This makes it useful for relative positioning. DIV, an older structural element for dividing pages into sections, is block-level (with line breaks before and after) and thus better suited for absolute positioning.

You may have already guessed the punch line. Since you can place content at will, you can even superimpose it. There was a good reason Netscape called its original version "layers": With CSS-P, Web pages have become something like interactive Photoshop files. To the x- and y- axes of the traditional HTML canvas, positioning adds a z-axis, or stacking order, which you may control and change through each layer's z-index. In fact, not only the stacking order, but a layer's visibility, position, clipping area, background, and even its content may be changed without rewriting or reloading the whole page.

Working with DHTML

To get a feel for how DHTML works in a real situation, let's produce some actual dynamic code. And let's assume that we're coding for the Web, which is to say for both major browsers. This means I can't take advantage of Microsoft's considerable DHTML enhancements, such as alpha channels, transition filters, and cursor styles. It also means I'll avoid techniques that trip on browser bugs - particularly in the halfhearted Macintosh version of Netscape 4.

Just to warn you, the going is liable to get fairly technical here. If you're completely new to scripting, or aren't much interested in the innards of DHTML, you may wish to skip ahead to this article's ultimate section, on software development tools.

For this demonstration, we'll build a small animation or slide show that loops through a series of images. It will begin playing as soon as the page is loaded, with an interframe delay of five seconds. Sound like a GIF animation? Not exactly. Some of the images are JPEGs, and we'll stagger them using CSS-P. We'll also provide a button to stop and start the show and add a few other nice little features. The result will hardly be a showpiece, but it should demonstrate enough DHTML features to get you started.

First we assemble our assets. As you can see, they're all different sizes - another advantage over the GIF animation. Two are transparent GIFs, which will reveal the background color, and three are JPEGs.

Since we'll want to display the images one at a time, while hiding the others, we need to put each in a separate layer. The best way to do this is with ID selectors in the document's style sheet. For example, we'll put the first image, image1.gif, in a layer object named image1:


<HTML>
<HEAD>
<TITLE>Slide Show</TITLE>
<STYLE Type="text/css">
<!--
#image1 { position: absolute; top: 120px;


left: 100px; visibility: visible; }


-->
</STYLE>
</HEAD>
<BODY BgColor="#000000" Text="#f6e483">
<DIV ID=image1>

<IMG SRC="image1.gif" WIDTH=96 HEIGHT=96>


</DIV>
</BODY>
</HTML>

This simply positions the first image 120 pixels from the top and 100 pixels from the left edge of the page, and specifies one additional property: visibility. This property controls exactly what you'd expect: whether the layer is visible or hidden. (A third alternative, visibility: inherit, instructs the browser to hide or show a layer when its parent is hidden or shown.)

The code for the other four image layers is nearly identical, except that we want to modify their CSS-P properties to change their position and hide them:


#image2 { position: absolute; top: 100px; left: 300px;
visibility: hidden; }
#image3 { position: absolute; top: 120px; left: 150px;
visibility: hidden; }
#image4 { position: absolute; top: 150px; left: 200px;
visibility: hidden; }
#image5 { position: absolute; top: 70px; left: 300px;
visibility: hidden; }
...
<DIV ID=image2>
<IMG SRC="image2.jpg" WIDTH=268 HEIGHT=196>
</DIV>
<DIV ID=image3>
<IMG SRC="image3.jpg" WIDTH=264 HEIGHT=148>
</DIV>
[etc.]

Hiding these layers when the page first loads is achieved by setting their default visibility to hidden. But how do we change that property later, when we wish to hide one image and show the next? Through JavaScript, which, in the 4.0 browsers, maintains an array of all the document's layers.

Here we run into our first browser discrepancy. Back when Netscape was still developing their LAYER tag, they defined a JavaScript layers] array, which is a property of the document object. Though entries in this array could, as in any other array, be accessed by index, the best way was to access each entry by name. To get to our image1 layer, for example, you would use this syntax:

document.layers['image1']

As a literal, the reference may be shortened to document.image1, since every named object is also a direct property of document. Either way, and the first way is safer, this expression returns a layer object, which has numerous readable and writeable properties, for example:

document.layers['image1'].left // the layer's "left" value
document.layers['image1'].zIndex // its stacking order
document.layers['image1'].document // its contents
document.layers['image1'].visibility // its visibility
document.image1.visibility // the shorter form

This notation is pretty cumbersome, mostly because it's based on the old LAYER syntax, where it made a little more sense and where there was some logic to an expression like document.layers['image1'].document. images[0].src. And the values of these expressions aren't always what you'd expect. For example, the value of document.layers['image1']. visibility is not visible, but rather show, another LAYER remnant.

Microsoft takes a different approach. There's no specific layers object in Explorer, but there is the object all, a meta-object or collection that contains all a document's element objects. Anything with a name - set with either a NAME or an ID attribute - may be accessed through all, like this:

document.all['image1']

which may be dramatically shortened, in a literal context, to the very brief image1. Some of the savings is made up in an extra object property, called style, which is required to get at the layer's style attributes:

image1.style.left; // the left position
image1.style.visibility; // "visible" or "hidden"

Such differences between the browsers abound, and they can quickly grow maddening. But for most simple DHTML, they're easily managed. Take the case where we wish to hide the layer image1 and display the layer image2. Here's how we do that in Netscape:

document.layers['image1'].visibility = 'hide';
document.layers['image2'].visibility = 'show';

And here's how we do it in Explorer:

document.all['image1'].style.visibility = 'hidden';
document.all['image2'].style.visibility = 'visible';

To satisfy both browsers with the same code, we need to do a little testing. The details are a bit tedious, but here's how it would work: First, check to see if the browser recognizes document.all. If it does, it's Explorer. If, on the other hand, it recognizes document.layers, then it's Netscape:

/* Assume a 4.0 browser; check to see if it's

Explorer */
var ie = false; // Assume it's not;

if (document.all) // but if this is true,
ie = true; // then it is Explorer

By checking the value of ie along the way, we can write out the appropriate code to get our job done:

function hideLayer(layer) {
/* The "layer" argument is a string */
if (ie) // for Explorer
document.all[layer].style.visibility = 'hidden';
else // for Netscape
document.layers[layer].visibility = 'hide';
}

We can write a function of this sort to show and hide our layers in sequence, which is easy since their names also form a sequence. If we start out with a variable set to 1, indicating that the layer image1 is visible, all we need to do is increment that variable each time we change the slide:

var tracker = 1; // index of the visible layer
// Test the browser
var ie = false;
if (document.all) ie = true;
function nextImage() {
/* Construct layers names for the visible layer and
the next layer */
/* First, identify the visible layer, whose ID is the
string "imageX," where "X" is the value of "tracker" */
var visibleLayer;
if (ie)
visibleLayer = document.all['image' +
tracker].style;
else
visibleLayer = document.layers['image' + tracker];

/* Now identify the next layer. If tracker is less than 5, add one to find the index; if tracker is 5, go back to the first image: */
if (tracker < 5) tracker++; // increment tracker
else tracker = 1;
var nextLayer;
if (ie)
nextLayer = document.all['image' + tracker].style;
else
nextLayer = document.layers['image' + tracker];

/* Now hide the "visibleLayer" and show
the "nextLayer": */
visibleLayer.visibility = (ie) ? 'hidden' : 'hide';
nextLayer.visibility = (ie) ? 'visible' : 'show';
}

Toward the end of this code we make use of the conditional operator (?:), which tests the expression in parentheses, returning the next expression if true or the one after if false. Here, if the browser is Explorer, we set the visibility to hidden or visible; if it's Navigator, we set it to hide or show.

To make this function repeat itself after five seconds, we need only add one more line, with a call to setTimeout. Since we want the user to be able to cancel the loop at any point, we need to store this timeout in a global variable; we'll call it rerun:

var rerun;
...
function nextImage() {
...
rerun = setTimeout('nextImage()',5000);
}

Now we need to start the show, which we could do by explicitly calling nextImage() at the end of the SCRIPT block. But to ensure the best results, we should wait until all the images are in the browser's cache, which will be true when the page has fully loaded. JavaScript traps this event, and we can respond to it via the onLoad event handler, which applies to the BODY element. In this case, we trap the load event to set off our nextImage() function:

<BODY BgColor="#000000" Text="#f6e48e"
onLoad="nextImage();">

Finally, we need to add a button to the page - or rather, two superimposed buttons, one labeled Stop and the other Start. (Button labels cannot be dynamically altered in Netscape, which is why we need two.) While the slide show is playing, only the Stop button will be visible; when the user cancels the show, the Stop button is hidden and the Start button revealed.

We go about this much as we did with the image layers, with absolute positioning and manipulation of the visibility property:

<DIV ID="stopButton" Style="position: absolute;
top: 70px; left: 100px; visibility: visible;">
<FORM><INPUT Type="button" Value="stop"
onClick="stopShow();"></FORM>
</DIV>
<DIV ID="startButton" Style="position: absolute;
top: 70px; left: 100px; visibility: hidden;">
<FORM><INPUT Type="button" Value="start"
onClick="startShow();"></FORM>
</DIV>

These two layers occupy the same space, but only one is visible at a time. Both their visibility and function is controlled through the onClick event handler, which is triggered when the user clicks on the visible button.

The button in the first layer calls a function that stops the slide show. This is achieved by canceling rerun, the variable storing our timeout loop. At the same time, it swaps the Start button in for the Stop button:

function stopShow() {
/* Make sure rerun is not null, and then clear it */
if (rerun) clearTimeout(rerun);
/* Build layer references for different browsers */
var stopLayer = (ie) ?
stopButton.style : document.stopButton;
var startLayer = (ie) ?
startButton.style : document.startButton;
/* Swap the layers */
stopLayer.visibility = (ie) ? 'hidden' : 'hide';
startLayer.visibility = (ie) ? 'visible' : 'show';
}

Conversely, startShow(), activated by a click on the Start button, resumes the process, starting from where we left off, and swaps back the buttons:

function startShow() {
/* Restart after a short delay */
rerun = setTimeout('nextImage()',50);
/* Build layer references for different browsers */
var stopLayer = (ie) ?
stopButton.style : document.stopButton;
var startLayer = (ie) ?
startButton.style : document.startButton;
/* Swap the layers */
stopLayer.visibility = (ie) ? 'visible' : 'show';
startLayer.visibility = (ie) ? 'hidden' : 'hide';
}

That wraps up the slide show, which is available for testing at www. atlasmagazine.com/dhtml/slide1.html. In building it I've made use of JavaScript, CSS-P, dynamic visibility, compatibility tests, and event handling - all key components of DHTML. But we haven't yet touched on its flashiest feature, and so far its most popular: dynamic positioning.

Everything you need to know about DHTML: intro

Part I: How to manage layers, cascading style sheets, and the inconsistencies between Mirosoft and Netscape browsers.

Part II: Static Positioning

Part III: Dynamic Positioning

Resources, references and where to find demos