reptile7's JavaScript blog
Monday, January 31, 2011
Further Adventures in Form Validation, Part 3
Blog Entry #204

We continue today our deconstruction of the Validator.js/utils.js scripts of HTML Goodies' "Bring Your Forms to Life With JavaScript" tutorial. In this post we'll get into the details of placing the check.gif and x.gif images respectively next to correctly and incorrectly filled-out fields in the liveForm form. The code described below works A-OK with all of the OS X GUI browsers on my computer.

Sticking with the document.forms["liveForm"].firstName.value = "Kris" example of the last couple of entries, let's say we are surfing with Firefox, for which the Kris input is valid. For all of the liveForm form's required fields excepting the email field, a valid input is signaled by a false value for the isEmpty variable of the Validator object's Validate( ) method. As noted at the end of the previous entry, the isEmpty=false value triggers a valid( ) method via an if (isEmpty) this.invalid( ); else this.valid( ); conditional at the end of the Validate( ) method.

Validator.valid = function( ) { ... }

valid( ) is the fourth method defined for Validator in the Validator.js script; it keeps tabs on the validation state of a given field and of the liveForm form in general. valid( )'s underlying function kicks off by calling the utils.js script's rc( ) function


and passing thereto this.currentSelector.invalidImage, which points to the object representing the <img src="x.gif"> element created earlier by the utils.js ce( ) function, and which is given a node identifier by the rc( ) declaration. The rc( ) function doesn't do anything in this run of the script but let's look at it briefly anyway:

function rc(node) {
    if (node != null) {
        try {
            node.parentNode.removeChild(node); }
        catch(err) {
            /* no node */ } } }

For the valid( ) method, the rc( ) function removes a previously inserted x.gif image upon changing an invalid field input to a valid field input; complementarily, Validator's invalid( ) method uses rc( ) to remove the check.gif image upon changing a valid field input to an invalid field input. rc( ) works its magic via the DOM's removeChild( ) method, which removes a child node from a parent node.

At this stage, this.currentSelector.invalidImage/node is a free-floating object that is not part of the index.html DOM tree and thus doesn't have a parentNode property; in this case, rc( )'s node.parentNode.removeChild(node); command would ordinarily throw a node.parentNode is null TypeError, which is preempted by placing the command in the try clause of a try...catch statement. (I speculated two entries ago that it wasn't necessary for utils.js's gebid( ) function to have a try...catch statement, but rc( ) definitely needs one.) The if (node != null) { ... } conditional wrapper strikes me as redundant, or at least I can't think of any contingencies in which node would be null, but if it ever were, then the try...catch statement could take care of the situation.

Moving back to the valid( ) function, the rc( ) function call is followed by a call to utils.js's InsertAfter( ) function:

InsertAfter(this.currentSelector.parentNode, this.currentSelector.validImage, this.currentSelector);
function InsertAfter(parent, node, referenceNode) {
    parent.insertBefore(node, referenceNode.nextSibling); }

InsertAfter( ) is the function that actually places the check.gif image next to the Kris-holding firstName field. The InsertAfter( ) function call sends three parameters to InsertAfter( ):
(1) this.currentSelector.parentNode, which points to the p element parent

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

of the firstName field;
(2) this.currentSelector.validImage, which points to the object representing the <img src="check.gif"> element created earlier by the utils.js ce( ) function; and
(3) this.currentSelector, which points to the firstName field (more specifically, to the field's underlying input element).
InsertAfter( ) gives to these parameters parent, node, and referenceNode identifiers, respectively.

InsertAfter( )'s insertBefore( ) command inserts the node image as a child into the calling parent paragraph just before the referenceNode field's nextSibling. As shown above, the referenceNode field doesn't actually have a nextSibling and consequently the referenceNode.nextSibling expression returns null; as a result, the node image is placed at the end (i.e., becomes the last child) of the parent paragraph. Got all that?

Moving back to the valid( ) function once again, the InsertAfter( ) function call is followed by a conditional that increments the value of the fieldNumValidated property of the Validator object:

if (!this.currentSelector.isValid) {
    this.fieldNumValidated++; }

Validator.currentSelector.isValid was earlier initialized to false by Validator's preload( ) method, and therefore the !this.currentSelector.isValid if condition returns true.
Validator.fieldNumValidated was earlier initialized to 0 by Validator's Initialize( ) method, so it's 1 now.

The preceding conditional is followed by another conditional that takes stock of where we are vis-à-vis validation of the form as a whole:

if (Validator.AllFieldsValidated( )) {
    gebid(this.submitId).disabled = false; }

This conditional does or does not activate the liveForm form's submit button depending on the true/false return of Validator's AllFieldsValidated( ) method, which is called by the if condition.

Validator.AllFieldsValidated = function(override) {
    if (this.fieldNumValidated >= this.fieldNumToValidate || override) return true;
    else return false; }

AllFieldsValidated( ) is the seventh (last) method defined for Validator in the Validator.js script; it compares the values of Validator's fieldNumValidated and fieldNumToValidate properties, returning true if the former is greater than or equal to the latter and false otherwise. (Recall that Validator.fieldNumToValidate represents the total number of fields in the liveForm form that must be filled out correctly, and was earlier set to 6 by Validator's Initialize( ) method.)

"What's with the override argument?"

Your guess is as good as mine. AllFieldsValidated( ) is called at two points in the tutorial code: (1) by Validator's valid( ) method and (2) by the liveForm form's onsubmit event handler:

<form id="liveForm" action="" method="post"
onsubmit="if (!Validator.AllFieldsValidated( )) return false;">

In neither case is a parameter passed to AllFieldsValidated( ). However, a detailed look at the this.fieldNumValidated >= this.fieldNumToValidate || override AllFieldsValidated( ) if condition shows that override, which evaluates to undefined in that condition, does not interfere with what AllFieldsValidated( ) is meant to do: green-light or red-light either the activation of the button or the submission of the liveForm form.

Relational operators have a higher precedence than does the logical-OR operator, so the this.fieldNumValidated >= this.fieldNumToValidate operation is carried out first. If we've only filled out the firstName field, then this comparison (1 >= 6) returns false; the overall condition now simplifies to a false || undefined operation. The || operator returns its second operand if its first operand cannot be converted to true, and therefore false || undefined returns undefined. As an if condition, undefined converts to false and consequently the else clause returns false to the AllFieldsValidated( ) function call.

Conversely, if we've correctly filled out all of the required fields in the liveForm form, then this.fieldNumValidated will have incremented to 6 and the this.fieldNumValidated >= this.fieldNumToValidate comparison will return true. A true || undefined operation returns true and consequently the if clause returns true to the AllFieldsValidated( ) function call.

The valid( ) function concludes with the following statements:

this.currentSelector.isValid = true;
this.currentSelector.validated = true;

For our ongoing Kris/firstName example, the first assignment toggles this.currentSelector.isValid to true (it would reassign true to this.currentSelector.isValid if we were changing a valid input to another valid input). The second assignment defines for the firstName field a custom validated property, setting it to true; no subsequent use is made of this property.

It's not valid

Now, what if we were surfing with Safari 5.0.3, for which the Kris/firstName input is not valid? In this case the Validate( ) method calls Validator's invalid( ) method:

Validator.invalid = function( ) {
    InsertAfter(this.currentSelector.parentNode, this.currentSelector.invalidImage, this.currentSelector);
    if (this.currentSelector.isValid) {
        this.fieldNumValidated--; }
    gebid(this.submitId).disabled = true;
    this.currentSelector.isValid = false;
    this.currentSelector.validated = true; }

invalid( ) is the fifth method defined for Validator in the Validator.js script; you should be able to handle its deconstruction as it complements the valid( ) method. Again, it is the utils.js InsertAfter( ) function that will place the x.gif image next to the firstName field.

In the following entry we will check over the Validator.js code that validates the liveForm form's email field.


Comments: Post a Comment

<< Home

Powered by Blogger

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