reptile7's JavaScript blog
Thursday, July 21, 2011
The Loopless World
Blog Entry #221

In this post we will take up HTML Goodies' "Checking Your Work: Validating Input -- Getting Started" and "Checking Your Work: Validating Input -- The Cool Stuff" tutorials, which are both authored by Curtis Dicken. These tutorials address some basic validation issues as they pertain to the "guestbook" form shown in the following image:

[The 'Checking Your Work: Validating Input' guestbook form]
(I put the word "guestbook" in quotes because the form doesn't have a textarea field for user comments, which IMO is a sine qua non of a guestbook.)

We've done the form validation thing several times previously, and the tutorial material is mostly review but I would still like to go through it for a couple of reasons:
(1) Both tutorials contain 'code bloat' that desperately needs to be tightened up.
(2) The "Cool Stuff" tutorial carries out regular expression-based validations of the form's text input values but provides no deconstruction at all therefor, and we really oughtta do something about that, y'know?

Before we go any further, I should point out a coding mistake that once corrected allows the rest of the code to run unproblematically: the type attribute of the form's button

<input id="ButtonSubmit" type="button" value="Submit" />

is set to button, which makes the button a generic push button and not a submit button; set type="submit" and we're good to go.

Don't leave it blank, part 5

The Creating a Test Form section of the "Getting Started" tutorial presents the guestbook form's HTML. Subsequently a Required Input section provides a script that runs through the text input fields and tests if any of them are blank by comparing the lengths of their values to 0. If one or more of those fieldObject.value.length == 0 comparisons return true, then the script pops up a 'Such and such is missing' alert( ) message and cancels the submission of the form.

The script code is contained by a validateMyForm( ) function that is called by the form's onsubmit="return validateMyForm( );" attribute when the button is clicked. The validateMyForm( ) function begins by initializing
(a) a valid variable for monitoring the validation of the form, and
(b) a validationMessage variable that will hold the aforementioned end-of-validation alert( ) message.

function validateMyForm( ) {
    var valid = true;
    var validationMessage = "Please correct the following errors:\r\n";

These declarations are followed by the validation code for the First Name field:

// Validate first name
if (document.getElementById("TextFirstName").value.length == 0) {
    validationMessage = validationMessage + " - First name is missing\r\n";
    valid = false; }

If the user doesn't enter anything into the id="TextFirstName" field, then - First name is missing\r\n will be appended to the validationMessage string and valid will be toggled to false.

The validateMyForm( ) function continues in this fashion with analogous conditionals for the Last Name, Telephone, Email, and How many years have you been using the internet? fields. At the end of the function,
(1) an if (valid == false) window.alert(validationMessage); conditional displays validationMessage on an alert( ) box if necessary, and
(2) a return valid; statement green-lights or red-lights form submission, as appropriate.

The \r\n at the end of each validationMessage segment translates to a single end-of-line character for most of the OS X browsers on my computer. If you were to leave everything blank, here's what the alert( ) box would look like with these browsers:

[The validationMessage alert( ) if all fields are blank]

IE 5.2.3 for Mac OS X interprets \r\n as two end-of-line characters, thereby giving a double-spaced validationMessage. Replacing \r\n with \n or \n\n should ensure a single-spaced or double-spaced display, respectively.

Is it necessary to have separate conditionals for the various text input fields? Certainly not. These tests are easily automated as follows:

var fieldData = ["First name", "Last name", "Telephone", "Email", "Years using Internet"];
var inputList = document.getElementsByTagName("input");
for (i = 0; i < 5; i++) {
    if (!inputList.item(i).value) {
        validationMessage += " - " + fieldData[i] + " is missing\n";
        valid = false; } }

• We don't need to go all the way out to the length property to check if a field is blank; if the field's value is an empty string, then it'll convert to false in a boolean context, and that false can be toggled to true by the ! logical operator - see the "Logical Operators" page of the Mozilla JavaScript Reference.
• By arraying the differing parts of its segments, the validationMessage string can be assembled automatedly as well.

To supplement the validationMessage alert( ), the author recommends in the tutorial's Adding Error Messages to the Form section that we display a '*Please provide your whatever' message next to each invalid field. These messages would be deployed in initially hidden span elements appended to the fields they are flagging

First Name: <input id="TextFirstName" type="text" />
<span id="errorFirstNameMissing" style="visibility:hidden;">*Please provide your first name.</span><br />

and then visibilized by switching their visibility settings to visible in their validateMyForm( ) validation conditionals.

if (document.getElementById("TextFirstName").value.length == 0) {
    validationMessage = validationMessage + " - First name is missing\r\n";
    document.getElementById("errorFirstNameMissing").style.visibility = "visible";
    valid = false; }
else {
    document.getElementById("errorFirstNameMissing").style.visibility = "hidden"; }

/* The else clause rehides the message upon going from a blank first name value to a non-blank first name value. */

We can again automate the manipulation of span visibility:

var spanList = document.getElementsByTagName("span");
for (j = 0; j < spanList.length; j++)
    spanList.item(j).style.visibility = !inputList.item(j).value ? "visible" : "hidden";

/* The ?: conditional operator is detailed here. */

Finally, the tutorial's Required Input – Dropdown List Box Style section attends to the validation of the form's Country selection list via a validateMyForm( ) conditional that checks if the user has not chosen one of the Canada, France, United Kingdom, or United States country options:

// Validate country selected
if (document.getElementById("SelectCountry").value == "Select your country...") {
    validationMessage = validationMessage + " - Please select your country\r\n";
    valid = false; }

In classical JavaScript the client-side Select object did not have a value property; however, the W3C has equipped the DOM HTMLSelectElement interface with a value attribute, so the if condition comparison is legit.

Automated validation can include the Country selection list
(a) if the fields of the form are accessed via the form's elements[ ] collection, and
(b) if the value attribute of the selection list's Select your country... option is set to an empty string:

var fieldData = ["First name", "Last name", "Telephone", "Email", "Country", "Years using Internet"];
for (i = 0; i < 6; i++) {
    if (!document.forms[0].elements[i].value) {
        validationMessage += " - " + fieldData[i] + " is missing\n";
        valid = false; } }
<select id="SelectCountry">
<option value="" selected="selected">Select your country...</option>

Checkbox assay

As long as we're tightening things up, let's jump forward to the Custom Validation section of the "Cool Stuff" tutorial and its validation of the Favorite websites? set of checkboxes. To determine whether at least one checkbox is checked, the section codes an if statement whose condition discretely tests the checked property of each checkbox.

if (document.getElementById("CheckboxHtmlGoodies").checked == false &&
document.getElementById("CheckboxWDVL").checked == false && // ... etc.

If the checked returns of the checkboxes are all false, then a relevant string is tacked onto validationMessage and valid is switched to false. This gets the job done but is a bit tedious. Alternatively we can loopically tally the number of unchecked checkboxes and then reset validationMessage and valid if the unchecked count reaches 5:

var unchecked = 0;
for (k = 7; k < 12; k++) if (!inputList[k].checked) unchecked++;
if (unchecked == 5) {
    validationMessage += " - Please select at least one favorite Web site option\n";
    valid = false; }

So far, so normal. However, per its title the section also considers a custom validation issue, one that we have not encountered previously. The Favorite websites? set of checkboxes has a bit of radio button character in that the final None selection and the first four checkbox selections are meant to be mutually exclusive, that is, if you check the None checkbox, then you're not supposed to check any of the other checkboxes. To police this situation, the section appends to the 'if no checkboxes are checked' if statement an else if clause that pits
(a) the checked status of the None checkbox against
(b) the checked status of the first four checkboxes as a group

else if (document.getElementById("CheckboxNone").checked == true &&
(document.getElementById("CheckboxHtmlGoodies").checked == true ||
document.getElementById("CheckboxWDVL").checked == true || // ... etc.

and subsequently resets validationMessage and valid if both (a) and (b) return true.

My own approach to the 'if you choose None, don't choose anything else' validation - an approach that facilely extends to the 'make sure at least one checkbox is checked' validation - is given below:

var checkedOther = false;
for (k = 7; k < 11; k++) if (inputList[k].checked) { checkedOther = true; break; }

var checkedNone = inputList[11].checked ? true : false;

if (checkedOther && checkedNone) {
    validationMessage += " - None should not be selected with other favorite Web site selections\n";
    valid = false; }

if (!checkedOther && !checkedNone) {
    validationMessage += " - Please select at least one favorite Web site option\n";
    valid = false; }

Of course, to merely check if a field is blank or not is to set the bar rather low. Accordingly, the Validating Data Type and Properly Formatted Input sections of the "Cool Stuff" tutorial present regular expressions and accompanying code blocks for vetting the values of the text input fields. But as intimated at the outset of the post, the author unloads this code on the reader without explaining it in any way, and he doesn't exactly encourage the reader to learn more about it:
Don’t worry if the expressions don’t make any sense to you. Creating regular expressions is an art form all its own. Whenever possible you should try to find an expression that someone else has already created and tested. Writing one from scratch can be both time consuming and tedious, especially for the novice.
Admittedly, I can see why he wouldn't want to explicate the emailExpression and telephoneExpression regexp patterns, but he should at least quickly go through the simpler noNumbersExpression and numbersOnlyExpression patterns - for example, he could say, "The ^[a-zA-Z]+$ pattern matches a string that, from start (^) to finish ($), comprises one or more (+) alphabetic characters, lowercase or uppercase (a-zA-Z)" - and then perhaps exhort the reader to do further homework/seek out a suitable reference for the other patterns. Is that too much to ask?

In Blog Entry #117, I stated, To anyone who wants to 'go beyond the basics' of Web coding, I highly recommend a study of regular expressions, which I have found pays major dividends in grappling with the minutiae of the coding world, and I very much stand by this assessment. The best regular expressions resource that I know of (even better than the Regular Expressions chapter in the Mozilla JavaScript Guide) is Jan Goyvaerts' - do check it out.

We'll put the "Cool Stuff" regexp validations under the microscope in the following entry.


Comments: Post a Comment

<< Home

Powered by Blogger

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