reptile7's JavaScript blog
Wednesday, March 23, 2011
 
Elastic Ball, Everlastic Ball
Blog Entry #209

Today's post returns us to the topic of animation as we take up HTML Goodies' "How to Create a JavaScript Animation" tutorial, which was authored by Lisha Sterling and, like the last two tutorials we've analyzed, originally appeared at WebReference.com.

We were introduced to JavaScript animation by HTML Goodies' JavaScript Primers #28 ("Putting it all together: Animation"), which we covered in Blog Entry #45. Subsequently in Blog Entry #147 we discussed and demonstrated a Netscape "JavaScript animation" example in response to a comment on HTML Goodies' "So, You Want a SlideShow, Huh?" tutorial. Both of these occasions featured a script that automatedly fills a fixed img placeholder with a cyclic series of images.

In contrast, "How to Create a JavaScript Animation" addresses the automated translational movement of an image. The author draws an analogy between the tutorial's code and the stop-motion animation that underpins motion pictures and television, but I would argue that 'live action' is a better description of what we'll be doing as we really will be moving an img placeholder from point A to point B across the screen.

The tutorial's first page begins with some general remarks about animation and its translation to the Web, and then briefly discusses JavaScript's two tools for carrying out an automative process in a controlled manner (vis-à-vis the uncontrolled operation of a for/while loop):
(1) the setTimeout( ) method of the window object; and
(2) the setInterval( ) method of the window object.
We are quite well-versed in the setTimeout( ) method at this point (we used it in both of our prior animation excursions, e.g.) but have in fact never made use of the setInterval( ) method before; the tutorial's demos all employ the setInterval( ) method so perhaps we should chat about it a bit.

The setInterval( ) method invokes a function or line of code every time the specified amount of time passes, quoting Microsoft. There are two syntaxes for the setInterval( ) method; the syntax utilized by the tutorial code is:

var intervalID = window.setInterval(codeString, delay);
// We'll see the other syntax below.


The above command will execute the codeString every delay milliseconds; this repeating execution can be stopped by feeding the intervalID return to a window.clearInterval( ) command:

window.clearInterval(intervalID);

Contra the tutorial, intervalID is not an "object" but a low-value integer (0, 1, 2 ..., depending on the browser) that, interestingly, undergoes incrementation upon re-calling the original setInterval( ) command.

The setTimeout( ) method goes back to JavaScript 1.0, the first version of JavaScript; setInterval( ) is a later-generation method that (on the Netscape side) was implemented in JavaScript 1.2. According to irt.org, Microsoft support for setInterval( ) began with MSIE 4, so it's possible that Microsoft developed setInterval( ) before Netscape did. Specification-wise, the window object's various timer methods had a "DOM Level 0" status back in 2011 but they are now part of HTML5.

The tutorial's second and third pages provide code and accompanying demos for four related animations:
(1) Animation #1 moves a small image of a ball (ball.gif) horizontally from left to right indefinitely, i.e., the image's movement does not stop at the right edge of the viewport.
(2) Animation #2 moves the ball.gif image horizontally from right to left indefinitely.
(3) Animation #3 moves the ball.gif image horizontally from left to right and stops that movement at the right edge of the viewport.
(4) Animation #4 moves the ball.gif image in a diagonal, bouncing, stays-in-the-viewport, somewhat-speed-randomized manner that, the author notes at the end of the tutorial, is reminiscent of the motion of the ball in the classic Pong video game (play it here).

Unfortunately, the tutorial does not offer a code download package; however, the code for the first three animations is combined/externalized in a balls.js script. The ball.gif image is located here; alternatively, you can download it [A small orange ball] from me here and now (right-click on the ball and then choose the Save Image As... or equivalent command from the contextual menu that pops up). At no point does the tutorial detail the ball.gif image's underlying HTML - a crucial omission, as we'll see below.

Animation #1

(N.B. In following the tutorial's demo instructions, you will find it much easier to track the ball.gif image's position on the page if you keep your browser's "zoom level" at 100%, i.e., don't increase it from its default setting.)

Let me begin by giving you that underlying HTML as it appears in the source of the tutorial's second page (OK, I've tweaked it a bit):

<img id="ball1" src="/img/2007/11/ball.gif" alt=""
style="position: absolute; left: 200px; top: 390px; visibility: hidden;">


Initially hidden, the ball.gif image is visibilized by clicking the Click here link in the Making Things Move section's first paragraph

<a href="javascript:viewBall( );">Click here</a>

which calls the balls.js script's viewBall( ) function

function viewBall( ) {
    document.images["ball1"].style.visibility = "visible"; }


which switches the image's CSS visibility property to visible.

The now-visible ball.gif image (more specifically, its upper-left-hand corner) is situated 200px to the right of the document's left edge and 390px below the document's top edge. The tutorial's second page contains three anchors that when clicked vertically drop the image via a showmeBall( ) function

function showmeBall(yAxis) {
    document.images["ball1"].style.top = yAxis + "px"; }


to demo locations for Animations #1, #2, and #3, respectively.

Let's go to and click the first of these anchors, a Bring the Ball Here so we can see it... link

<a href="javascript:showmeBall('820');">Bring the Ball Here so we can see it...</a>
<!-- It's not necessary to quote the 820, which will be stringified when it's concatenated with the px unit identifier. -->


that increases the image's style.top value to 820px. To the right of the anchor is a Try it link

<a href="javascript:var t = window.setInterval('goRight( )', 80);">Try it</a>

that when clicked calls the Animation #1 goRight( ) function

function goRight( ) {
    document.images["ball1"].style.left = parseInt(document.images["ball1"].style.left) + 5 + "px"; }


which make[s] the ball move 5 pixels to the right every 80 milliseconds and whose code is given above the first anchor.

The author's discussion of goRight( )'s style.left-setting statement could definitely use some help.
But what's with the parseInt( ) around document.images["ball1"].style.left? The actual content of the variable document.images["ball1"].style.left is an object not a number, so we need to extract the number out of the object in order to perform a mathematical function on it. parseInt( ), as I'm sure you've guessed by now, parses the integer out of a variable.
document.images["ball1"].style.left is not an "object" but a reference expression for a CSS property. And when read or written dynamically, the values of all CSS properties, without exception, have a string data type - see the CSS2 Extended Interface section of the DOM Level 2 Style Specification.

The top-level parseInt( ) function acts on a string and will extract an integer from that string if and only if the integer appears at the beginning of the string, e.g., parseInt( ) will extract 200 from 200px but not from px200 (for which parseInt( ) would return NaN) - it does not generally extract an integer "out of" a string.
There's one more gotcha you should know about before we move on. In order to use the style position addresses to move an object around the window, you need to give the item an explicit style to start with. You can either declare that style in a CSS style sheet, in the HTML itself, or to declare starting style coordinates in your JavaScript.
Ouch! It is not true that the goRight( ) function would be able to read the image's style.left value if we were to set the image's position-related properties in a CSS style sheet:

#ball1 { position: absolute; left: 200px; top: 390px; }
/* However, we can set the initial visibility value in this way as we won't have to read that value later. */


See the Remarks section of Microsoft's style object page for more on this. On the other hand, we can indeed read these properties if we set them "in the HTML itself" (as is done above, and that's why the image HTML should be specified in the tutorial text) or if we set them JavaScriptically:

function positionBall( ) {
    document.getElementById("ball1").style.position = "absolute";
    document.getElementById("ball1").style.left = "200px";
    document.getElementById("ball1").style.top = "390px"; }
window.onload = positionBall;


In sum: We extract the integer at the beginning of the image's style.left setting (200px), add 5 to it, and tack on a px unit identifier to give an incremented style.left setting (205px), whose assignment to document.images["ball1"].style.left moves the image 5 pixels to the right; do this over and over again every 80 milliseconds and you have a 'go right' animation.

Additional comments

(1) For calling a function, the first setInterval( ) parameter can be specified as a function pointer (vis-à-vis a stringified function call), a syntax Mozilla recommends:

var t = window.setInterval(goRight, 80);

(2) In the goRight( ) function, the parseInt(document.images["ball1"].style.left) + 5 + "px" expression is interpreted left to right, so there's no need to parenthesize the arithmetic part thereof.

(3) I see that Mozilla now recommends that we always specify (emphasis in original) the parseInt( ) radix parameter to eliminate reader confusion and to guarantee predictable behavior. Different implementations produce different results when a radix is not specified.

document.images["ball1"].style.left = parseInt(document.images["ball1"].style.left, 10) + 5 + "px";

(4) A relatively positioned image works just as well and would have made it easier for the author to coordinate the animation demos with the tutorial text. Try it below - clicking the button will visibilize the ball.gif image with a position:relative;left:0px; 'style address'.




(I use push buttons for my interface elements as it rubs me the wrong way to use links for non-link purposes.)

Animation #2

The Making Things Move section also hosts Animation #2. Clicking the Animation #2 Bring the Ball Here... demo anchor drops the image to top:1420px; at the HTML Goodies version of the tutorial and to top:1220px; at the WebReference version of the tutorial. (Why the discrepancy? I have no idea.) Clicking the adjacent Go Left link moves the image 5 pixels to the left every 80 milliseconds via a goLeft( ) function that repeatedly subtracts 5px from document.images["ball1"].style.left:

function goLeft( ) { document.images["ball1"].style.left = (parseInt(document.images["ball1"].style.left) - 5) + "px"; }
/* The parentheses surrounding goLeft( )'s subtraction operation are unnecessary; the right side of the statement is again interpreted left to right. */


We'll go through Animations #3 and #4 in the following entry.

reptile7

Sunday, March 13, 2011
 
Append to Prepend
Blog Entry #208

We wind up our treatment of HTML Goodies' "Bring Your Forms to Life With JavaScript" tutorial with one last topic.

The utils.js ac( ) function

The non-default clauses of the first Validate( ) switch statement aren't the only parts of the original Validator.js/utils.js scripts that don't see any action: as noted in the "Code Overview" section of Blog Entry #202, at no point in the tutorial code is the utils.js ac( ) function ever called. Let's look at that function, shall we?

function ac( ) {
    if (ac.arguments.length > 1) {
        var a = ac.arguments[0];
        for (i = 1; i < ac.arguments.length; i++) {
            if (arguments[i]) {
                a.appendChild(ac.arguments[i]); } }
        return a; }
    else {
        return null; } }


ac( ) is named for the appendChild( ) method, which it features; appendChild( ) is another method that (like removeChild( ) and insertBefore( )) was introduced with the Node interface of the DOM Level 1 Core. I suspect that ac( ) was a forerunner of the utils.js InsertAfter( ) function, i.e., the author originally aimed to use ac( ) to append <img src="check.gif">/<img src="x.gif"> elements to the required fields of the liveForm form. Interestingly, however, if we were to replace the InsertAfter( ) calls of the Validator object's valid( ) and invalid( ) methods with corresponding ac( ) calls

ac(this.currentSelector.parentNode, this.currentSelector.validImage, this.currentSelector); // For the valid( ) method
ac(this.currentSelector.parentNode, this.currentSelector.invalidImage, this.currentSelector); // For the invalid( ) method


then the check.gif/x.gif images would be prepended to the selectors they are 'bringing to life'.

Your first name: *


Here's a brief rundown of what happens:

(1) An if statement first checks if ac( ) has more than one argument: true.
FYI: The arguments object is no longer a child object of the core JavaScript Function object (this relationship was deprecated by JavaScript 1.4), i.e., the ac. parts of the ac.arguments expressions in the statement can and should be removed.

(2) The first-in-source-order ac( ) argument, this.currentSelector.parentNode (the p element parent of the currentSelector we're validating), is given an a identifier.

(3) A two-iteration for loop begins.
(a) The loop's first iteration appends ac( )'s arguments[1], this.currentSelector.validImage (<img src="check.gif">) or this.currentSelector.invalidImage (<img src="x.gif">), to a's list of children. For a valid firstName input, we now have:

<p>Your first name: * <br /><input type="text" id="firstName" name="firstName" onblur="Validator.Validate(this);" /><img src="check.gif"></p>

(b) The loop's second iteration moves ac( )'s arguments[2], this.currentSelector, from its current position to the end of a's list of children; continuing with our valid firstName example, we end up with:

<p>Your first name: * <br /><img src="check.gif"><input type="text" id="firstName" name="firstName" onblur="Validator.Validate(this);" /></p>

In sum, via appendChild( )-ing first the check.gif/x.gif image and then the to-be-validated field, ac( )'s overall effect is to insert the marker image before the field (i.e., between the post-label br element and the field) - something we could actually accomplish with a single line of code:

arguments[0].insertBefore(arguments[1], arguments[2]);

Complementarily, we can append the marker image to the to-be-validated field with:

arguments[0].appendChild(arguments[1]);

Pretty simple, huh? But not simple enough to resurrect the Validator.js/utils.js scripts in favor of the code we've developed in the last couple of entries. (You can just as easily prepend the marker image as append it using the image visualization approach of Blog Entry #206, although an appended image looks better IMO.)

We won't be covering the next three Beyond HTML : JavaScript sector tutorials.

(1) "Building Simulations with JavaScript"
I can't figure out what's going on in this poorly fleshed-out article (its promise of "applying real data" notwithstanding), so I'm not going to discuss it.

(2) "How to Create Remote Ajax Requests"
Another Kris Hadlock production, this advanced tutorial is beyond where we are, so to speak, and is maybe even beyond the scope of this blog. (However, the Beyond HTML : JavaScript sector also features a "How to Develop Web Applications with Ajax: Part 1" tutorial, which we will probably check over later.)

(3) "Object-Oriented JavaScript"
The first two pages of this article offer an abstract discussion of objects and object-oriented programming, much of it unrelated to JavaScript. The article's third page goes over some advanced aspects of JavaScript functions; this page contains some code samples and provides links to demos for some of those samples, but the material here isn't meaty enough to write about.

The sector tutorial after that, "How to Create a JavaScript Animation", is much more up our alley and will complement our past forays into the animation area, so we'll put it under the microscope in the following entry.

reptile7

Saturday, March 05, 2011
 
Further Adventures in Form Validation, Part 6
Blog Entry #207

We conclude our analysis of HTML Goodies' "Bring Your Forms to Life With JavaScript" tutorial with two 'lagniappe' topics.

The non-default clauses of the first Validate( ) switch statement

The liveForm form's set of required fields comprises five <input type="text"> fields and a select-one selection list. As noted in Blog Entry #203, the first switch statement of the Validator object's Validate( ) method contains case clauses for dealing with radio buttons, checkboxes, and select-multiple selection lists: how might we incorporate these control types as required fields into the liveForm form? Before we do that, however, we'd better make sure those clauses are OK in the first place...

radio

Here's the radio case as it appears in the Validator.js script:

case 'radio':
    for (var x = 0; x < selector.length; x++) {
        if (selector[x].checked == true) {
            isEmpty = false; } }
    break;


Suppose we add

Your age group: *
Under 25
25-44
45-64
65+

as a radio button-based field to the liveForm form and that each button's underlying input element is equipped with an onblur="Validator.Validate(this);" event handler. We check one of the buttons and then tab to the next field (in practice, browsers vary wildly in their radio button tabbing behavior, but for the moment let's assume we're using a browser that lets us do this), triggering Validator.Validate( ). Fast-forwarding to Validate( )'s first switch statement, selector.type evaluates to radio and the radio case fires - so far, so good.

The radio clause is designed to loop through a set of radio buttons and toggle the isEmpty variable to false if any of them are checked. Accordingly, the loop's selector-based expressions - selector.length and selector[x].checked - expect selector to represent a set (or to use the argot of the DOM, a NodeList) of radio buttons. However, if we pass this to Validate( ), then we are sending to Validate( ) an individual radio button object, for which selector.length will evaluate to undefined; as a result, the loop's x < selector.length condition returns false from the get-go, isEmpty remains at true, and a false negative occurs: Validator.invalid( ) is called and the x.gif image is placed next to the checked radio button.

We can solve this problem by recasting the for loop as:

for (var x = 0; x < selector.form.elements[selector.name].length; x++) {
    if (selector.form.elements[selector.name][x].checked)
        isEmpty = false; }

/* checked is itself a boolean property and thus the '== true' part of the if condition is unnecessary. */

The selector.form.elements[selector.name] reference allows us to access the collection of radio buttons having the same name as the selector radio button. The referencing of radio button sets and individual radio buttons is briefly discussed here in the JavaScript 1.3 Client-Side Reference.

checkbox, select-multiple

Upon feeding this to Validate( ), the checkbox and select-multiple cases both work fine as far as they go, so we'll deal with them together. The checkbox clause is quite a bit simpler than the radio clause as it only applies to one checkbox at a time.

case 'checkbox':
    if (selector.checked) {
        isEmpty = false; }
    break;


The select-multiple clause is almost identical to the radio clause: it loops through a select-multiple selection list's options and toggles isEmpty to false if any of them are selected.

case 'select-multiple':
    for (var x = 0; x < selector.length; x++) {
        if (selector[x].selected == true) { /* Again, the '== true' part of the condition is unnecessary. */
            isEmpty = false; } }
    break;


The client-side Select object does have a length property that [r]eflects the number of options in the selection list, so we don't just skip over the for loop body this time. More interesting is the loop's if condition: the options[ ] collection of the Select object is the standard interface for iteratively going through a set of selection list options, and the selector[x] expression might not seem like a valid way to access those options (at least I've never seen this syntax for referencing Option objects before), but it does do so, so there you have it.

undefined

I suppose I should mention that the radio clause is preceded by a one-line case 'undefined': break; clause. I don't know why the author included this clause in the switch statement (as some sort of error catcher, perhaps?) and it does seem pointless at first glance, but could we make use of such a clause if we wanted to? Sure - in particular, we can use it to 'bring to life' non-control inline elements that don't normally have a type attribute. Consider the span element below:

<span id="spanID" onmouseover="Validator.Validate(this);">Isn't this cool?</span>

We can give this element a custom type attribute set to the string undefined:

document.getElementById("spanID").type = "undefined";

Next, let the undefined switch case toggle isEmpty to false:

case 'undefined': isEmpty = false; break;

Now, mousing over the Isn't this cool? span element text will place the check.gif image next to that text - cool indeed.

Practical implementation

Let's get back to our "Your age group *" set of radio buttons. If we respectively place the radio button labels after the radio buttons (as is standard practice), and if we use the utils.js InsertAfter( ) function to mark our radio button input (we shouldn't, but bear with me for a moment), then the check.gif or x.gif image will be inserted between a button and its label:

Under 25

This will also be true for a corresponding set of checkboxes. Maybe you're comfortable with this: I don't like it.

Another issue arises if we make a radio button selection and then change our selection to another button: check.gif markers will be inserted, and remain, next to both selections, i.e., the check.gif marker next to our initial selection will not be removed.

Under 25
25-44

For a corresponding set of checkboxes, we will at least see x.gif and check.gif markers respectively next to our initial and subsequent selections in this case. These marker location/redundancy issues are not relevant to a select-multiple selection list, which is marked by InsertAfter( ) with a single, baseline-aligned image (the select and option elements have highly restrictive content models and cannot have img element children).

À la a select-multiple selection list, it would IMO be better to mark a uninamed set of radio buttons or checkboxes with only one check.gif/x.gif image; moreover, it would look nicer if that image lined up horizontally with the check.gif/x.gif images that mark the form's other required fields. Drawing inspiration from the "Dummy Form" of HTML Goodies' "How to Populate Fields from New Windows Using JavaScript" tutorial, which we worked through not so long ago, we can do this as follows:

(1) Wrap each radio button/checkbox and its label in a block-level, width:90%; label element.
(2) Precede the first label element with a zeroed-out, right-floated img placeholder that will later be visibilized and filled with a check.gif or x.gif image, which will vertically align with the first radio button/checkbox and its label; this approach can also be used to top-align the image that marks a select-multiple selection list.

label { display: block; width: 90%; }
.imageClass2 { display: none; float: right; }
...
<p>Your age group: *<br />
<img id="ageGroupImage" class="imageClass2" src="" alt="" />
<label><input type="radio" name="ageGroup" value="Under 25" onclick="testField(this);" /> Under 25</label>
<label><input type="radio" name="ageGroup" value="25-44" onclick="testField(this);" /> 25-44</label>
...


For radio buttons and checkboxes, I find that onclick is supported by all of the OS X GUI browsers on my computer whereas onblur is not supported by Safari and Chrome, so we'll use the former for our validation trigger.

At this point we cut loose from the Validator.js/utils.js code and move to the testField( ) function that underpins the demo of the previous entry. Upon tweaking it a bit, my retooled radio for loop (vide supra) can be used to set a true/false validCondition for both radio buttons and checkboxes:

selectorList = selector.form.elements[selector.name];

if (selector.name == "ageGroup" || selector.name == "checkboxName") {
    for (var x = 0; x < selectorList.length; x++) {
        if (selectorList[x].checked) {
            validCondition = true;
            break; }
        else validCondition = false; } }


An analogous conditional can be written for a select-multiple selection list.
• For checkbox and select-multiple selection list fields: If we make a selection and subsequently deselect that selection, then the else clause will toggle validCondition to false and thereby prevent a false positive from occurring.
• For radio button, checkbox, and select-multiple selection list fields: Without the if clause's break statement, the else clause will produce a false negative if we make a choice and it isn't the last-in-source-order choice.

Demo

The demo below sports a modified liveForm form in which the original form's street text field, state selection list, and zip text field have been respectively replaced by the ageGroup radio button field, a software checkbox field, and a tutorials select-multiple selection list (the fieldNumToValidate remains at 6) - for these new fields, feel free to choose and unchoose selections as you see fit/are able. The firstName, lastName, and email text fields are regulated by the same regular expressions that regulated them in the previous entry's demo.

* = required field
Registration form
Your first name: *


Your last name: *


Your email: *


Company:


Your age group: *

What types of computer software do you regularly use? (Check all that apply.) *

I am interested in tutorials on the following topics (select all that apply): *

 
N.B. To prevent the serial checking of radio buttons and checkboxes from running up the fieldNumValidated count, and because Safari and Chrome would not 'remember' the value of a custom isValid property for the radio button and checkbox collections, it was necessary to define radioOK and checkboxOK variables if (!radioOK) radioOK = false; if (!checkboxOK) checkboxOK = false; for respectively keeping track of the selection states of the radio button and checkbox collections as a whole. A common checkValidationStatus( ) function assays selector.isValid, radioOK, and checkboxOK, and relates these expressions to fieldNumValidated. function checkValidationStatus(testExpression) {     if (validCondition && !testExpression) fieldNumValidated++;     else if (!validCondition && testExpression) fieldNumValidated--;     testExpression = validCondition ? true : false;     return testExpression; } if (selector.type == "text" || selector.type == "select-multiple")     selector.isValid = checkValidationStatus(selector.isValid); else if (selector.type == "radio")     radioOK = checkValidationStatus(radioOK); else if (selector.type == "checkbox")     checkboxOK = checkValidationStatus(checkboxOK);
Let me put off my other lagniappe topic, the utils.js ac( ) function, until the next entry.

reptile7


Powered by Blogger

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