reptile7's JavaScript blog
Thursday, August 02, 2012
Schindleria Præmatura
Blog Entry #260

In today's post I will detail my fishdemo2 approach to the Swimming Fish Example's Changing the Stacking Order of Fish and Poles animation and how it differs from the example's fish2css.htm approach.



I find that Netscape 4.x straightforwardly applies the CSS position property to textual elements (paragraphs, headings, links, etc.) and to the div and span container elements but that its application of position to more complex elements is problematic to a greater or lesser extent. For example, Netscape 4.x will position a ul or ol list but upon doing so the resulting list item markers are invariably discs, regardless of the list's type setting.

Apropos the Changing the Stacking Order animation, with Netscape 4.x:
(1) position/top/left declarations have no effect at all on img elements;
(2) a form { position: absolute; top: 100px; left: 10px; } rule set bizarrely causes the button-containing form to disappear (poof, gone).
This is why the fish and pole images and the interface form are wrapped in layer or span containers. Fortunately, the current, standard CSS 2.1 position property applies to all elements and is supported by all modern (and more than a few not-so-modern) GUI browsers, and I have accordingly sent the layer/span wrappers packing and transferred their ids/styles to their children with the exception of the fishB span/img, which I've thrown out altogether.

A new interface

I have added to the animation UI a button that when clicked stops the fish's movement; updated code for this button appears at the end of the post. (Of course, some users may want to watch the fish swim indefinitely. ;-)) Moreover, I've traded in the parent form of the and buttons for a more semantically appropriate div container. Are we submitting anything to a processing agent? We're not submitting anything to a processing agent.

Netscape 4.x won't render controls outside of a form and doesn't support the button element, but again, we're not coding for Netscape 4.x anymore.

Validation note

A body element with img element children will pass a transitional validation but not a strict validation.
• In the Transitional DTD, the body element has a (%flow;)* +(INS|DEL) content model.
• In the Strict DTD, the body element has a (%block;|SCRIPT)+ +(INS|DEL) content model.
If you want to carry out a strict validation of either Swimming Fish Example animation, then you'll have to wrap the fish and pole images in a div, and I've done that in the fishdemo2 code. Or you could just do a transitional validation. The necessary document type declarations for these validations are detailed here.


fish2.gif preload

If we've chucked the fishB span/img, then how are we going to preload the fish2.gif image? This can and should be done the traditional way, i.e., via an Image object constructor:

var fishB = new Image( );
fishB.src = "fish2.gif";
/* An analogous fishA object can be created for the fish1.gif image: */
var fishA = new Image( );
fishA.src = "fish1.gif";

Controlling properties redux

The fish2css.htm JavaScript uses five functions to move the fish layer to and fro and thread it through the pole layers: initializeFish( ), movefish2( ), changeDirection( ), changePoles( ), and resetPoles( ). The linchpin of this functionality is the fish layer's custom direction property, which indirectly determines
(1) the fish layer's direction of movement,
(2) the source of the image held by the fish layer, and
(3) the z-stacking order of the fish and pole layers.

In the course of considering how to consolidate the changePoles( ) and resetPoles( ) functions, it occurred to me that we could 'cut out the middleman' in that (2) the src of the fish image, now held by an id="fish2" img element, could be used to determine (1) the fish2 img's direction of movement and (3) the z-stacking order of the fish2/pole imgs. Toward this end I defined a boolean isForwardFish variable that evaluates to true if the swimming fish is fish1.gif and to false if the swimming fish is fish2.gif.

var fish2 = document.getElementById("fish2"), isForwardFish;
function movefish2( ) {
    isForwardFish = /fish1/.test(fish2.src); ... }

The isForwardFish definition compares a fish1 regular expression literal with the fish2.src string via the test( ) method of the RegExp object. Alternatively, an equivalent comparison of fish2.src and the string fish1 can be carried out via the indexOf( ) method of the String object:

isForwardFish = fish2.src.indexOf("fish1") != -1;

Stack it

With the isForwardFish flag in hand, we can use the ?: conditional operator to recast the resetPoles( )/changePoles( ) functionality as:

var bluepole2 = document.getElementById("bluepole2");
var greenpole2 = document.getElementById("greenpole2");
var redpole2 = document.getElementById("redpole2");
function movefish2( ) { ... = isForwardFish ? "1" : "4"; = isForwardFish ? "2" : "3"; = isForwardFish ? "3" : "2"; = isForwardFish ? "4" : "1"; ... }

• Unlike the layerObject.moveAbove( ) method, the CSS z-index property has (and has long had) cross-browser support.

• The zIndex variant of the z-index property is not part of CSS but is actually an attribute of the Style DOM's CSS2Properties interface; the style object itself implements the Style DOM's CSSStyleDeclaration interface.

• If isForwardFish is true, then the above zIndex statements produce the same z-stacking order as that resulting from the fishdemo2 source, i.e., the blue pole is on the bottom, the green pole is above the blue pole, the fish is above the green pole, and the red pole is on top; if isForwardFish is false, then the z-stacking order is reversed.

• All CSS2Properties attribute values have a DOMString type and are typed as strings regardless of what you assign to them by most JavaScript engines; in practice, integers assigned to zIndex can be stringified or not, per your preference.

Right and left we go

In conjunction with the fishA/fishB Image objects, the isForwardFish flag also allows us to throw out the changeDirection( ) function and formulate the fish movement code as: = "0px";
var leftnumber2, timerID2;
function movefish2( ) { ...
    leftnumber2 = parseInt(, 10);
    if (isForwardFish) {
        if (leftnumber2 < 450) = (leftnumber2 + 5) + "px";
        else fish2.src = fishB.src; }
    else {
        if (leftnumber2 > 10) = (leftnumber2 - 5) + "px";
        else fish2.src = fishA.src; }
    timerID2 = window.setTimeout("movefish2( );", 30); }

With the fishA.src and fishB.src expressions respectively replacing the original fish.forwardimg and fish.backwardimg pointers and with isForwardFish replacing fish.direction, we obviously don't need the initializeFish( ) function either.

In sum, we don't need five functions to run the Changing the Stacking Order animation: a single movefish2( ) function based on the source of the fish image and supported by a small set of preceding global statements will do the job.

In the name of completeness

Once the fish movement is under way, reclicking the button will (a) trigger a second movefish2( ) cycle and (b) approximately double the speed of the fish because the assignments will be executed twice as often per 30-millisecond interval; additional clicks further speed up the fish. You can limit the fish movement to a single movefish2( ) cycle as follows:

(1) Globally declare an isMoving variable and set it to true at the beginning of the movefish2( ) function body.

var isMoving; function movefish2( ) { isMoving = true; ... }

(2) Code the animation UI as:

#buttondiv { position: absolute; top: 100px; left: 10px; }
<div id="buttondiv">
<button type="button" onclick="if (!isMoving) movefish2( );">Move the fish</button>
<button type="button" onclick="window.clearTimeout(timerID2); isMoving = false;">Chill out, fish</button>

The !isMoving condition returns true for the first click but false for subsequent clicks, thereby stopping the latter dead in their tracks.

In the following entry we'll move on to the Nikki's Diner Example of the DHiNC resource.

Comments: Post a Comment

<< Home

Powered by Blogger

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