reptile7's JavaScript blog
Wednesday, March 14, 2012
Rotator Redux
Blog Entry #244

We return to the topic of animation in today's post with a look at HTML Goodies' "Adding Rotating Images to Your Web Site" tutorial, which is authored by Michael Rohde. The "Rotating Images" tutorial offers a script that cycles array items through a common div placeholder; in the tutorial the script works with a set of linking images, although there's no reason that you couldn't use the script with other types of content, as points out in the tutorial comment thread.

Rotator deconstruction

Here's the author's placeholder HTML:

<layer id="placeholderlayer"></layer><div id="placeholderdiv"></div>

I see you rolling your eyes: "The layer element??" The "Rotating Images" tutorial went live in May 2010, and it's definitely pretty weird that the author felt a need to reach out to Netscape 4.x users, who are the ONLY folks who would have support for the layer element/object, because there shouldn't be anyone out there using Netscape 4.x at this point, there really shouldn't. But whatever. In the "Notes on invalid documents" section of the HTML 4.01 Specification the W3C prescribes, If a user agent encounters an element it does not recognize, it should try to render the element's content. The placeholderlayer layer holds no content and accordingly is ignored by non-Netscape 4.x browsers; layout-wise it collapses into nothingness à la an empty div element.

The linking images array has the following format:

var items = new Array( );
items[0] = "<a href='link.htm'><img alt='image0 (9K)' src='/Images/image0.jpg' height='300' width='300' border='0' /></a>";
items[1] = "<a href='link.htm'><img alt='image1 (9K)' src='/Images/image1.jpg' height='300' width='300' border='0' /></a>"; // Etc.

Ugh, stringified HTML (whose unescaped </a sequences would prevent the main document's validation) - we'll retool this data later, but for now:

• There are six items elements in all; you are of course free to increase or decrease the number of elements.

• The link targets (which are all specified as link.htm but, per the tutorial text, should be ordinalized: link0.html, link1.html, etc.) are placed in the same directory that holds the main document whereas the image files are placed in a separate directory; as the /Images/image#.jpg relative URL seems to cause some confusion for the newbies in the comment thread, it would probably be a better idea to put everything in the same directory.

• The border attribute of the img element is deprecated; we should use an a img { border: none; } style declaration to ensure that the images are borderless.

The linking images are rotated by a rotater( ) (sic) function that is called when the main document loads. Strangely, two rotater( ) functions appear in the tutorial code; the first of these functions is given below:

var howOften = 5;
var current = 0;
function rotater( ) {
    document.getElementById("placeholder").innerHTML = items[current];
    current = (current == items.length - 1) ? 0 : current + 1;
    window.setTimeout("rotater( );", howOften * 1000); }
window.onload = rotater;

The progress of the rotation is tracked by a current variable, which is initialized to 0. Suppose for a moment that the placeholder div's id is placeholder and not placeholderdiv. The above function initially gets the placeholder div and sets its innerHTML to items[0], the image0.jpg/link0.html linking image. Next, a ?: conditional statement increments current to 1. Finally, a setTimeout( ) command re-calls the function after 5 (howOften) seconds.

In the second rotater( ) iteration, the image1.jpg/link1.html linking image is loaded into the placeholder div; current is incremented to 2; rotater( ) is re-called after a 5-second delay; and so on. The ?: statement resets current to 0 if current has reached items.length - 1 (5).

The first rotater( ) function would be perfectly adequate for all modern JavaScript-supporting GUI browsers; in practice it is overwritten by its more elaborate successor:

var ns6 = document.getElementById && !document.all;
function rotater( ) {
    if (document.layers) {
        document.placeholderlayer.document.close( ); }
    if (ns6)
        document.getElementById("placeholderdiv").innerHTML = items[current];
    if (document.all)
        placeholderdiv.innerHTML = items[current];
    current = (current == items.length - 1) ? 0 : current + 1;
    window.setTimeout("rotater( );", howOften * 1000); }

The second rotater( ) function's body begins with a conditional that writes items[0] to the document of the placeholderlayer layer if the browser supports document.layers, which as noted earlier is only true for Netscape 4.x. For those of you who are really curious, the client-side layer object and its document property are discussed in the "Using JavaScript With Positioned Content" chapter of Netscape's "Dynamic HTML in Netscape Communicator" resource.

After the layer code is a second if clause that sets the placeholderdiv div's innerHTML to items[0] for browsers with getElementById( ) support AND whose document.all return cannot be converted to true; this clause is operative for the Mozilla browsers, Chrome/Safari, and Opera.

For document.all
Firefox, Camino, and Netscape 9 return [object HTML document.all class],
Chrome and Safari return [object HTMLAllCollection], and
Opera returns [object HTMLCollection];
unorthodoxly, all of these values convert to false in a logical context (thus leading to a true return for the ns condition) - see Opera developer Hallvord Steen's "!document.all == true" blog post for more on this.

The aforelisted browsers do in fact implement the all( ) collection; for example, they all give red and bolded text with:
<div id="div1">This is some div text.</div>
<script type="text/javascript">
document.all("div1").style.color = "red";
document.all("div1").style.fontWeight = "bold";

However, IE is the only major browser for which the document.all return ([object Collection] with IE 5.1.6) converts to true in a logical context, and its users are flagged by a third if clause that sets placeholderdiv.innerHTML to items[0]. In a Note at the end of Example Two in its "Introduction to Dynamic HTML" resource, Microsoft explains:
Note When Windows Internet Explorer encounters a tag that defines an id or name, it creates a reference to it in the global scope so that it can be easily located by script; however, this is considered non-standard behavior. To ensure the widest browser support for your DHTML, use getElementById( ) to locate target elements.
Its non-standard status notwithstanding, the ability of id and name attribute values to double as object references is now a cross-browser feature: the non-IE OS X GUI browsers on my computer all support it. (IE 4.5 running in the SheepShaver environment also supports it, and thus it was actually a cross-platform feature from the get-go.)

The second rotater( ) function concludes with the same current-adjusting conditional and recursive rotater( ) function call that the first one does.

Streamline it

In its original form the "Rotating Images" script effectively cycles three things:
(1) the img src value;
(2) the img alt value; and
(3) the link href value.
It is really necessary to cycle entire elements through the placeholderdiv div? Not at all. A more efficient approach is to use a linking img itself as the common placeholder

<a id="anchorID" href="link0.html" target="_blank">
<img id="imageID" width="48" height="48" src="image0.gif" alt="image0 (9K)" />

and then cycle the src/alt/href data through that placeholder. The script code below can be used to organize the data and to preload the rotating images:

var theImages = new Array( );
var linkURLs = new Array( );
for (i = 0; i < 6; i++) {
    theImages[i] = new Image( );
    theImages[i].src = "image" + i + ".gif";
    theImages[i].alt = "image" + i + " (9K)";
    linkURLs[i] = "link" + i + ".html"; }

With theImages and linkURLs arrays in place, all that remains is to update the rotator function as follows:

var current = 0, howOften = 5;
function rotator( ) {
    document.getElementById("imageID").src = theImages[current].src;
    document.getElementById("imageID").alt = theImages[current].alt;
    document.getElementById("anchorID").href = linkURLs[current];
    current = (current == theImages.length - 1) ? 0 : current + 1;
    window.setTimeout("rotator( );", howOften * 1000); }
window.onload = rotator;

(If you like, you can respectively access the placeholder's img and anchor elements with classical JavaScript's images[ ] and links[ ] collections so that those legions of Netscape 4.x users can continue to run your code.)

The "Rotating Images" tutorial doesn't provide a demo, which saddened commenter Rob. Well, I would never want to sadden anyone if I could help it:

image0 (4 KB)

Demo changes

• The howOften delay has been cut to 2.

• The theImages.srcs are set discretely as the uploaded images have irregular URLs.

• The anchorID links point to relevant entries in Merriam-Webster's online dictionary.
Some of the tutorial commenters ask for things that we've covered on prior occasions - e.g., AMP wants to pause the rotation with a mouseover, Tara wants a slide show - and it is tempting to respond to these people (given that the HTML Goodies management evidently can't be bothered to respond to them), but I think our time is better spent moving on to the next Beyond HTML : JavaScript sector tutorial, "Top 10 JavaScript Snippets for Common Tasks", in the following entry.


Comments: Post a Comment

<< Home

Powered by Blogger

Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)