Chapter 7

Form Validation


CONTENTS

When a user enters data into the fields of a form in your Web page, it's entirely possible that the data might not make sense. The user may enter a numeric value that is too low or too high. The user may enter text where you expect a number, or vice versa. The user may misspell text. The user may skip a required field. There are probably as many ways to enter invalid data as there are ways to write forms.

It should not come as a surprise, then, that an important step in creating a form is making sure that what the user entered makes sense. That step is called form validation.

Creating a form that validates input-regardless of whether you use JavaScript-requires a CGI (Common Gateway Interface) program on the server. The Web page is only a front end to the server.

The scenario

Suppose your boss has asked you to create a Web page from which customers can order computer equipment. You need to collect the customer's name, address, phone number, age, credit card information, and what the customer wants to order.

The requirements

The customer must have a first and last name; a middle initial is optional. The customer must have a street address. The customer must reside in the United States and must provide either a 5-digit zip code or a 5+4 zip code. The customer must supply a daytime phone number, including area code. The customer must provide his or her age, and must be at least 18. The customer must supply a credit card number, credit card type (Visa or MasterCard), and date of expiration.

The customer may order any of the following: HAL-470 computer ($2,000), Banana9000 computer ($3,000), high-resolution monitor ($800), low-resolution monitor ($50), deluxe keyboard ($250), regular keyboard ($40), laser printer ($2,000), inkjet printer ($600), dot-matrix printer ($200), mouse ($100), trackball ($125), or scanner ($500). The customer may not order more than $5,000 worth of equipment.

The solution (without JavaScript)

To create a Web page form, you must first choose the kinds of input fields you need. To capture the customer's name, INPUT TEXT elements are the obvious choice. Let's make the first and last names 20 characters each and the middle initial 1 character.

The address is next. A TEXTAREA element, 2 rows of 30 characters each, will suffice for the street address, and INPUT TEXT elements will work for the city, state, and zip codes-20 characters for the city, 2 characters for the state, and 10 for the zip code.

For the customer's daytime phone number, use three INPUT TYPE elements-three characters for the area code, three characters for the office code, and four characters for the remainder of the phone number.

A three-character INPUT TEXT element will suffice for the customer's age-we could get an order from someone older than 99.

The credit card information is easy: Four INPUT TEXT elements for the credit card number, four characters each; an INPUT RADIO element to select between Visa and MasterCard; and two INPUT TEXT elements for the expiration date, two characters each.

You'll need a SELECT element for the merchandise, and it has to allow for multiple selections.

Finally, you'll need an INPUT SUBMIT element to send the data to the server, and an INPUT RESET element to allow the customer to start over.

Putting it all together, you have a Web page. Listing 7.1 shows the HTML and Figure 7.1 shows you what the form looks like on the screen.

Figure 7.1 : Non-JavaScript solution.


Listing 7.1: Non-JavaScript solution
<HTML>
    <HEAD>
        <TITLE>ComputoRama Order Form</TITLE>
    </HEAD>
    <BODY>
        <FORM ACTION="mailto:mailhost@computoRama.usa.com" METHOD=POST>
            <TABLE BORDER="2" CELLPADDING="1">
                <TR>
                    <TD ROWSPAN="2">Who Are You?</TD>
                    <TD><INPUT TYPE="text" NAME="FirstName" SIZE=2Ø></TD>
                    <TD><INPUT TYPE="text" NAME="MiddleInitial" SIZE=1></TD>
                    <TD><INPUT TYPE="text" NAME="LastName" SIZE=2Ø></TD>
                    <TD><INPUT TYPE="text" NAME="Age" SIZE=3></TD>
                </TR>
                <TR>
                    <TD><FONT SIZE="-2">First Name</FONT></TD>
                    <TD><FONT SIZE="-2">MI</FONT></TD>
                    <TD><FONT SIZE="-2">Last Name></TD>
                    <TD><FONT SIZE="-2">Age</TD>
                </TR>
                <TR>
                    <TD ROWSPAN="3">How Do We Contact You?</TD>
                    <TD COLSPAN="4" VALIGN="TOP">Street Address: <TEXTAREA 
                    name="StreetAddress" rows=2 cols=3Ø></TEXTAREA></TD>
                </TR>
                <TR>
                    <TD COLSPAN="2">City: <INPUT TYPE="text" NAME="City" 
                    SIZE=2Ø></TD>
                    <TD COLSPAN="2">State: <INPUT TYPE="text" NAME= "State" 
                    SIZE=2></TD>
                </TR>
                <TR>
                    <TD COLSPAN="2">ZIP Code: <INPUT TYPE="text" NAME="ZIPCode" 
                    SIZE=1Ø></TD>
                    <TD COLSPAN="2">Daytime Phone
                        (<INPUT TYPE="text" NAME="Phone1" SIZE=3>)
                        <INPUT TYPE="text" NAME="Phone2" SIZE=3>-
                        <INPUT TYPE="text" NAME="Phone3" SIZE=4></TD>
                </TR>
                <TR>
                    <TD>Credit Card
                        <INPUT TYPE="radio" NAME="CreditCardType" VALUE="Visa" 
                        CHECKED>Visa
                        <INPUT TYPE="radio" NAME="CreditCardType" 
                        VALUE="MasterCard">M/C</TD>
                    <TD COLSPAN="2" ALIGN="CENTER">
                        <INPUT TYPE="text" NAME="CreditCardNumber1" SIZE=4>
                        <INPUT TYPE="text" NAME="CreditCardNumber2" SIZE=4>
                        <INPUT TYPE="text" NAME="CreditCardNumber3" SIZE=4>
                        <INPUT TYPE="text" NAME="CreditCardNumber4" SIZE=4></TD>
                    <TD COLSPAN="2">Expiration Date:
                        <INPUT TYPE="text" NAME="ExpirationMonth" SIZE=2>/
                        <INPUT TYPE="text" NAME="ExpirationYear" SIZE=2></TD>
                </TR>
                <TR>
                    <TD>Merchandise</TD>
                    <TD COLSPAN=4><SELECT MULTIPLE NAME="Merchandise" SIZE=1>
                        <OPTION SELECTED> HAL-47Ø <OPTION> Banana9ØØØ
                        <OPTION> High Res Monitor <OPTION> Low Res Monitor
                        <OPTION> Deluxe Keyboard <OPTION> Regular Keyboard
                        <OPTION> Laser Printer <OPTION> Inkjet Printer <OPTION> 
                        Dot Matrix Printer
                        <OPTION> Mouse <OPTION> Trackball
                        <OPTION> Scanner
                        </SELECT></TD>
                </TR>
                <TR>
                    <TD ALIGN=CENTER COLSPAN="5">
                        <H1>Thank You For Your Order!</H1>
                    </TD>
                </TR>
            </TABLE>
            <CENTER>
                <INPUT TYPE="submit" VALUE="Ship It!"> <INPUT TYPE="reset" 
                VALUE="Clear Entries">
            </CENTER>
        </FORM>
    </BODY>
</HTML>

The rest of the solution resides on the server, and is beyond the scope of this book. There will be a CGI (Common Gateway Interface) program on the server that will process the results when the customer presses the Ship It! button. That program will have to perform form validation and return an error to the customer if mandatory fields are omitted or mistyped. That is in addition to whatever work must be performed to process a valid order.

The solution (with JavaScript)

Using JavaScript, you can enhance the form shown in Figure 7.1 to perform much of the form validation on the customer's browser. The advantages of doing so are

So what can you do to add form validation to this Web page? Plenty.

First, get rid of the SUBMIT button. You can write an event handler to catch the submit event, and that event handler can prevent submission of the form. However, whether the customer's data is transmitted or not, the page will be cleared. If you make the SUBMIT button an ordinary BUTTON INPUT element and give it an ONCLICK event handler, you'll have better control over the screen. All the ONCLICK event handler has to do is perform the form validation and, if all the customer data is acceptable, call document.forms[0].submit().

Having created an event handler to perform forms validation, let's start with the customer's name. Recall that the customer must supply a first and last name. To verify this, you simply need to check that the fields have a nonzero length, like this:

function nameOK()
    {
    if (document.forms[ Ø ].FirstName.value.length == Ø)
        {
        alert("I need a first name, please");
        return false;
        }
    if (document.forms[ Ø ].LastName.value.length == Ø)
        {
        alert("I need a last name, please");
        return false;
        }
    return true;
    }

Then, in your "submit" button event handler, call the function:

function checkForm()
    {
    if (nameOK() == false)
        {
        return;
        }
    // other tests...
    document.forms[ Ø ].submit();
    }

The age field has to be filled in, and the age must be a number greater than or equal to 18. First, verify that the customer has entered anything. Then verify that the customer has entered a number: Use the substring method to get the first character in the field and verify that it is a digit. For that, you need a little routine:

function isDigit(c)
    {
    var test = "" + c;
    if (test == "Ø" || test == "1" || test == "2" || test == "3" || test == "4" 
    || test == "5" || test == "6" || test == "7" || test == "8" || test == "9")
        {
        return true;
        }
    return false;
    }

Finally, having verified that the customer entered something that starts with a digit, use the built-in function parseInt() to convert the customer's input and verify that the value is at least 18. Putting these steps together, you have something like this:

function ageOK()
    {
    if (document.forms[ Ø ].Age.value.length == Ø)
        {
        alert("Sorry, you have failed to enter an age");
        return false;
        }
    var c = document.forms[ Ø ].Age.value.substring(Ø, 1);
    if (isDigit(c) == false)
        {
        alert("Sorry, you have failed to enter an appropriate age");
        return false;
        }
    var result = parseInt(document.forms[ Ø ].Age.value, 1Ø);
    if (result < 18)
        {
        alert("Sorry, you have failed to enter an appropriate age");
        return false;
        }
    return true;
    }

As with NameOK(), you then add this to the checkForm() function:

function checkForm()
    {
    if (nameOK() == false)
        {
        return;
        }
    if (AgeOK() == false)
        {
        return;
        }
    // other tests...
    document.forms[ Ø ].submit();
    }

Now that the customer's name and age have been verified as present and acceptable, it's time to verify the address. Verifying the presence of the street address, city, and state fields is exactly like verifying the presence of the first and last name fields: Just check for a nonzero length of the field's value property. The zip code is a little more interesting, however. Recall that the zip code must be either a five-digit code or ZIP+4-5 digits, a hyphen, and four digits.

Because this is something you're going to do more than once, let's write a function to verify that a given string is composed entirely of digits. It should simply extract the characters from the string one by one and verify that each one is a digit:

function isAllDigits(s)
    {
    var test = "" + s;
    for (var k = Ø; k < test.length; k++)
        {
        var c = test.substring(k, k+1);
        if (isDigit(c) == false)
            {
            return false;
            }
        }
    return true;
    }

Then checking the zip code field is easy. First, check the length of the field value-it should be either 5 or 10. If the length is 5, call isAllDigits() for the value. If it's 10 digits, use the substring method to get the first 5 characters and verify that they're all digits. If they are, check the next character-it should be a hyphen-and then use the substring method to get the last four characters. Check that they're all digits as well. The function should look like this:

function zipOK()
    {
    var zip = document.forms[ Ø ].ZIPCode.value;
    if (zip.length == 5)
        {
        var result = isAllDigits(zip);
        if (result == false)
            {
            alert("Invalid character in zip code");
            }
        return result;
        }
    else if (zip.length == 1Ø)
        {
        var result = isAllDigits(zip.substring(Ø,5));
        if (result == true)
            {
            if (zip.substring(5,6) != "-")
                {
                result = false;
                }
            else
                {
                result = isAllDigits(zip.substring(6,1Ø));
                }
            }
        if (result == false)
            {
            alert("Invalid character in zip code");
            }
        return result;
        }
    else
        {
        alert("Invalid zip code; please re-enter it");
        return false;
        }
    }

Checking the phone number is now very easy. You simply verify that all three fields are filled in and that they're all digits.

Checking the credit card numbers is also easy; just verify that there are four digits in each of the four fields. But you can take it a step further. Most credit card numbers can be validated by using a rule called MOD 10, and for a given company, there is usually a fixed prefix or range of prefixes for the card number. All Visa card numbers begin with 4, and all MasterCard card numbers begin with 51, 52, 53, 54, or 55, and both numbers can be validated with the MOD 10 rule.

The MOD 10 rule says to scan the card number from right to left. Starting with the digit on the right, double every other digit as you move to the left. For the number 49927398716, this means you wind up with these numbers: 4 (9+9) 9 (2+2) 7 (3+3) 9 (8+8) 7 (1+1) 6, or 4 18 9 4 7 6 9 16 7 2 6. Doubled numbers that become 2-digit values are then replaced with the sum of the digits: 4 (1+8) 9 4 7 6 9 7 (1+6) 2 6, or 4 9 9 4 7 6 9 7 7 2 6. These digits are then added up; the sum should be evenly divisible by 10 (in this case, they add up to 70-the number passes).

Applying the MOD 10 rule to our credit card numbers means that the first and third digits of each set of four digits needs to be doubled. Rather than manipulate the digits of the result, you can write a function to get the doubled and digit-summed value of a given digit:

function doubleForMod1Ø(c)
    {
    var d = Ø + c;
    if (d == Ø) return Ø;
    if (d == 1) return 2;
    if (d == 2) return 4;
    if (d == 3) return 6;
    if (d == 4) return 8;
    if (d == 5) return 1; // 5+5 = 1Ø; 1+Ø = 1
    if (d == 6) return 3; // 6+6 = 12; 1+2 = 3
    if (d == 7) return 5; // 7+7 = 14; 1+4 = 5
    if (d == 8) return 7; // 8+8 = 16; 1+6 = 7
    return 9; // (digit must be 9) 9+9 = 18; 1+8 = 9
    }

Then, for the entire four digits, obtain the sum:

function sumForMod1Ø(s)
    {
    var v = parseInt(s, 1Ø); // get the value
    var result = doubleForMod1Ø(Math.floor(v / 1ØØØ));
    v = v % 1ØØØ;
    result += Math.floor(v / 1ØØ);
    v = v % 1ØØ;
    result += doubleForMod1Ø(Math.floor(v / 1Ø));
    v = v % 1Ø;
    result += v;
    return result;
    }

Putting all this together, let's validate the credit card number using the code shown in Listing 7.2.


Listing 7.2: The validateCreditCardNumber() function
function validateCreditCardNumber()
    {
    if (document.forms[ Ø ].CreditCardNumber1.value.length != 4)
        {
        alert("The credit card number is not completely filled out");
        return false;
        }
    if (document.forms[ Ø ].CreditCardNumber2.value.length != 4)
        {
        alert("The credit card number is not completely filled out");
        return false;
        }
    if (document.forms[ Ø ].CreditCardNumber3.value.length != 4)
        {
        alert("The credit card number is not completely filled out");
        return false;
        }
    if (document.forms[ Ø ].CreditCardNumber4.value.length != 4)
        {
        alert("The credit card number is not completely filled out");
        return false;
        }
    if (isAllDigits(document.forms[ Ø ].CreditCardNumber1.value) == false)
        {
        alert("The credit card number contains invalid characters");
        return false;
        }
    if (isAllDigits(document.forms[ Ø ].CreditCardNumber2.value) == false)
        {
        alert("The credit card number contains invalid characters");
        return false;
        }
    if (isAllDigits(document.forms[ Ø ].CreditCardNumber3.value) == false)
        {
        alert("The credit card number contains invalid characters");
        return false;
        }
    if (isAllDigits(document.forms[ Ø ].CreditCardNumber4.value) == false)
        {
        alert("The credit card number contains invalid characters");
        return false;
        }
    if (document.forms[ Ø ].CreditCardType[ 1 ].checked == true) // Visa
        {
        if (document.forms[ Ø ].CreditCardNumber1.value.substring(Ø,1) != "4")
            {
            alert("The credit card number is not valid; please re-enter it");
            return false;
            }
        }
    else // must be MasterCard
        {
        var prefix =
        parseInt(document.forms[Ø].CreditCardNumber1.value.substring(Ø,2));
        if (prefix < 51 || 55 < prefix)
            {
            alert("The credit card number is not valid; please re-enter it");
            return false;
            }
        }
    var sum = sumForMod1Ø(document.forms[ Ø ].CreditCardNumber1.value);
    sum += sumForMod1Ø(document.forms[ Ø ].CreditCardNumber2.value);
    sum += sumForMod1Ø(document.forms[ Ø ].CreditCardNumber3.value);
    sum += sumForMod1Ø(document.forms[ Ø ].CreditCardNumber4.value);
    if (sum % 1Ø != Ø)
        {
        alert("The credit card number is not valid; please re-enter it");
        return false;
        }
    return true;
 }

Verifying the expiration date is straightforward. Make sure the month and year fields are filled in (make sure they have nonzero lengths). Make sure month is a value from 1 to 12. Make sure month and year are at least a month in the future. Listing 7.3 shows how it works.


Listing 7.3: The dateOK() function
function dateOK()
    {
    if (document.forms[ Ø ].ExpirationMonth.value.length == Ø)
        {
        alert("You must fill in the expiration date");
        return false;
        }
    if (isDigit(document.forms[ Ø ].ExpirationMonth.value.substring(Ø,1)) == 
    false)
        {
        alert("Expiration date should be numeric");
        return false;
        }
    var eMonth = parseInt(document.forms[ Ø ].ExpirationMonth.value, 1Ø);
    if (eMonth < 1 || 12 < eMonth)
        {
        alert("Expiration date is out of range");
        }
    if (document.forms[ Ø ].ExpirationYear.value.length == Ø)
        {
        alert("You must fill in the expiration date");
        return false;
        }
    if (isDigit(document.forms[ Ø ].ExpirationYear.value.substring(Ø,1)) == false)
        {
        alert("Expiration date should be numeric");
        return false;
        }
    var eYear = parseInt(document.forms[ Ø ].ExpirationYear.value, 1Ø);
    if (eYear < 5Ø)
        {
        eYear += 2ØØØ;
        }
    else
        {
        eYear += 19ØØ;
        }
    var today = new Date(); // get today's date
    var thisYear = 19ØØ + today.getYear();
    var thisMonth = 1 + today.getMonth();
    if (eYear < thisYear)
        {
        alert("Your credit card seems to have expired");
        return false;
        }
    if (thisYear < eYear)
        {
        return true;
        }
    if (eMonth < thisMonth)
        {
        alert("Your credit card seems to have expired");
        return false;
        }
    if (thisMonth < eMonth)
        {
        return true;
        }
    alert("Your credit card has expired or is about to expire");
    return false;
 }

Finally, the last item: the merchandise selected. You'll need to make one minor modification to the OPTION elements and give them values so you can add them up. Then it's simply a matter of walking the list of options and tallying up the result. While you're checking for a result in excess of $5,000, you should also say something if the customer didn't order anything:

function merchandiseOK()
    {
    var tally = Ø;
    var optionCount = document.forms[ Ø ].Merchandise.options.length;
    for (var k = Ø; k < optionCount; k++)
        {
        if (document.forms[ Ø ].Merchandise.options[ k ].selected == true)
            {
            tally += parseInt(document.forms[ Ø ].Merchandise.options[ k ].value, 
            1Ø);
            if (5ØØØ < tally)
                {
                alert("Sorry, we cannot handle a transaction in excess of 
                $5,ØØØ.ØØ");
                return false;
                }
            }
        }
    if (tally == Ø)
        {
        alert("Sorry, you don't seem to have ordered anything");
        return false;
        }
    return true;
    }

Listing 7.4 shows the entire solution, using JavaScript. There is no separate figure for the JavaScript solution, because as far as the screen is concerned, there are no differences.


Listing 7.4: JavaScript solution for form validation
<HTML>
    <HEAD>
        <TITLE>ComputoRama Order Form</TITLE>
        <SCRIPT LANGUAGE="JavaScript">
<!-- hide the code!
function nameOK()
    {
    if (document.forms[ Ø ].FirstName.value.length == Ø)
        {
        alert("I need a first name, please");
        return false;
        }
    if (document.forms[ Ø ].LastName.value.length == Ø)
        {
        alert("I need a last name, please");
        return false;
        }
    return true;
    }

function isDigit(c)
    {
    var test = "" + c;
    if (test == "Ø" || test == "1" || test == "2" || test == "3" || test == "4" 
    || test == "5" || test == "6" || test == "7" || test == "8" || test == "9")
        {
        return true;
        }
    return false;
    }

function ageOK()
    {
    if (document.forms[ Ø ].Age.value.length == Ø)
        {
        alert("Sorry, you have failed to enter an age");
        return false;
        }
    var c = document.forms[ Ø ].Age.value.substring(Ø, 1);
    if (isDigit(c) == false)
        {
        alert("Sorry, you have failed to enter an appropriate age");
        return false;
        }
    var result = parseInt(document.forms[ Ø ].Age.value, 1Ø);
    if (result < 18)
        {
        alert("Sorry, you have failed to enter an appropriate age");
        return false;
        }
    return true;
    }

function isAllDigits(s)
    {
    var test = "" + s;
    for (var k = Ø; k < test.length; k++)
        {
        var c = test.substring(k, k+1);
        if (isDigit(c) == false)
            {
            return false;
            }
        }
    return true;
    }

function addressOK()
    {
    if (document.forms[ Ø ].StreetAddress.value.length == Ø)
        {
        alert("We need a street address");
        return false;
        }
    if (document.forms[ Ø ].City.value.length == Ø)
        {
        alert("We need a city");
        return false;
        }
    if (document.forms[ Ø ].State.value.length != 2)
        {
        alert("We need a 2-letter state abbreviation");
        return false;
        }
    return true;
    }

function zipOK()
    {
    var zip = document.forms[ Ø ].ZIPCode.value;
    if (zip.length == 5)
        {
        var result = isAllDigits(zip);
        if (result == false)
            {
            alert("Invalid character in zip code");
            }
        return result;
        }
    else if (zip.length == 1Ø)
        {
        var result = isAllDigits(zip.substring(Ø,5));
        if (result == true)
            {
            if (zip.substring(5,6) != "-")
                {
                result = false;
                }
            else
                {
                result = isAllDigits(zip.substring(6,1Ø));
                }
            }
        if (result == false)
            {
            alert("Invalid character in zip code");
            }
        return result;
        }
    else
        {
        alert("Invalid zip code; please re-enter it");
        return false;
        }
    }

function phoneOK()
    {
    if (document.forms[ Ø ].Phone1.value.length != 3)
        {
        alert("We need a phone number, including area code");
        return false;
        }
    if (document.forms[ Ø ].Phone2.value.length != 3)
        {
        alert("We need a phone number, including area code");
        return false;
        }
    if (document.forms[ Ø ].Phone3.value.length != 4)
        {
        alert("We need a phone number, including area code");
        return false;
        }
    if (isAllDigits(document.forms[ Ø ].Phone1.value) == false)
        {
        alert("Bad character in phone number");
        return false;
        }
    if (isAllDigits(document.forms[ Ø ].Phone2.value) == false)
        {
        alert("Bad character in phone number");
        return false;
        }
    if (isAllDigits(document.forms[ Ø ].Phone3.value) == false)
        {
        alert("Bad character in phone number");
        return false;
        }
    return true;
    }

function doubleForMod1Ø(c)
    {
    var d = Ø + c;
    if (d == Ø) return Ø;
    if (d == 1) return 2;
    if (d == 2) return 4;
    if (d == 3) return 6;
    if (d == 4) return 8;
    if (d == 5) return 1; // 5+5 = 1Ø; 1+Ø = 1
    if (d == 6) return 3; // 6+6 = 12; 1+2 = 3
    if (d == 7) return 5; // 7+7 = 14; 1+4 = 5
    if (d == 8) return 7; // 8+8 = 16; 1+6 = 7
    return 9; // (digit must be 9) 9+9 = 18; 1+8 = 9
    }

function sumForMod1Ø(s)
    {
    var v = parseInt(s, 1Ø); // get the value
    var result = doubleForMod1Ø(Math.floor(v / 1ØØØ));
    v = v % 1ØØØ;
    result += Math.floor(v / 1ØØ);
    v = v % 1ØØ;
    result += doubleForMod1Ø(Math.floor(v / 1Ø));
    v = v % 1Ø;
    result += v;
    return result;
    }

function validateCreditCardNumber()
    {
    if (document.forms[ Ø ].CreditCardNumber1.value.length != 4)
        {
        alert("The credit card number is not completely filled out");
        return false;
        }
    if (document.forms[ Ø ].CreditCardNumber2.value.length != 4)
        {
        alert("The credit card number is not completely filled out");
        return false;
        }
    if (document.forms[ Ø ].CreditCardNumber3.value.length != 4)
        {
        alert("The credit card number is not completely filled out");
        return false;
        }
    if (document.forms[ Ø ].CreditCardNumber4.value.length != 4)
        {
        alert("The credit card number is not completely filled out");
        return false;
        }
    if (isAllDigits(document.forms[ Ø ].CreditCardNumber1.value) == false)
        {
        alert("The credit card number contains invalid characters");
        return false;
        }
    if (isAllDigits(document.forms[ Ø ].CreditCardNumber2.value) == false)
        {
        alert("The credit card number contains invalid characters");
        return false;
        }
    if (isAllDigits(document.forms[ Ø ].CreditCardNumber3.value) == false)
        {
        alert("The credit card number contains invalid characters");
        return false;
        }
    if (isAllDigits(document.forms[ Ø ].CreditCardNumber4.value) == false)
        {
        alert("The credit card number contains invalid characters");
        return false;
        }
    if (document.forms[ Ø ].CreditCardType[ 1 ].checked == true) // Visa
        {
        if (document.forms[ Ø ].CreditCardNumber1.value.substring(Ø,1) != "4")
            {
            alert("The credit card number is not valid; please re-enter it");
            return false;
            }
        }
    else // must be MasterCard
        {
        var prefix = 
        parseInt(document.forms[Ø].CreditCardNumber1.value.substring(Ø,2));
        if (prefix < 51 || 55 < prefix)
            {
            alert("The credit card number is not valid; please re-enter it");
            return false;
            }
        }
    var sum = sumForMod1Ø(document.forms[ Ø ].CreditCardNumber1.value);
    sum += sumForMod1Ø(document.forms[ Ø ].CreditCardNumber2.value);
    sum += sumForMod1Ø(document.forms[ Ø ].CreditCardNumber3.value);
    sum += sumForMod1Ø(document.forms[ Ø ].CreditCardNumber4.value);
    if (sum % 1Ø != Ø)
        {
        alert("The credit card number is not valid; please re-enter it");
        return false;
        }
    return true;
    }

function dateOK()
    {
    if (document.forms[ Ø ].ExpirationMonth.value.length == Ø)
        {
        alert("You must fill in the expiration date");
        return false;
        }
    if (isDigit(document.forms[ Ø ].ExpirationMonth.value.substring(Ø,1)) == 
    false)
        {
        alert("Expiration date should be numeric");
        return false;
        }
    var eMonth = parseInt(document.forms[ Ø ].ExpirationMonth.value, 1Ø);
    if (eMonth < 1 || 12 < eMonth)
        {
        alert("Expiration date is out of range");
        }
    if (document.forms[ Ø ].ExpirationYear.value.length == Ø)
        {
        alert("You must fill in the expiration date");
        return false;
        }
    if (isDigit(document.forms[ Ø ].ExpirationYear.value.substring(Ø,1)) == false)
        {
        alert("Expiration date should be numeric");
        return false;
        }
    var eYear = parseInt(document.forms[ Ø ].ExpirationYear.value, 1Ø);
    if (eYear < 5Ø)
        {
        eYear += 2ØØØ;
        }
    else
        {
        eYear += 19ØØ;
        }
    var today = new Date(); // get today's date
    var thisYear = 19ØØ + today.getYear();
    var thisMonth = 1 + today.getMonth();
    if (eYear < thisYear)
        {
        alert("Your credit card seems to have expired");
        return false;
        }
    if (thisYear < eYear)
        {
        return true;
        }
    if (eMonth < thisMonth)
        {
        alert("Your credit card seems to have expired");
        return false;
        }
    if (thisMonth < eMonth)
        {
        return true;
        }
    alert("Your credit card has expired or is about to expire");
    return false;
    }

function merchandiseOK()
    {
    var tally = Ø;
    var optionCount = document.forms[ Ø ].Merchandise.options.length;
    for (var k = Ø; k < optionCount; k++)
        {
        if (document.forms[ Ø ].Merchandise.options[ k ].selected == true)
            {
            tally += parseInt(document.forms[ Ø ].Merchandise.options[ k ].value);
            if (5ØØØ < tally)
                {
                alert("Sorry, we cannot handle a transaction in excess of 
                $5,ØØØ.ØØ");
                return false;
                }
            }
        }
    if (tally == Ø)
        {
        alert("Sorry, you don't seem to have ordered anything");
        return false;
        }
    return true;
    }

function checkForm()
    {
    if (nameOK() == false)
        {
        return;
        }
    if (ageOK() == false)
        {
        return;
        }
    if (addressOK() == false)
        {
        return;
        }
    if (zipOK() == false)
        {
        return;
        }
    if (phoneOK() == false)
        {
        return;
        }
    if (validateCreditCardNumber() == false)
        {
        return;
        }
    if (dateOK() == false)
        {
        return;
        }
    if (merchandiseOK() == false)
        {
        return;
        }
    document.forms[ Ø ].submit();
    }
// end of code -->
        </SCRIPT>
    </HEAD>
    <BODY>
        <FORM ACTION="mailto:mailhost@computoRama.usa.com" METHOD=POST>
            <TABLE BORDER="2" CELLPADDING="1">
                <TR>
                    <TD ROWSPAN="2">Who Are You?</TD>
                    <TD><INPUT TYPE="text" NAME="FirstName" SIZE=2Ø></TD>
                    <TD><INPUT TYPE="text" NAME="MiddleInitial" SIZE=1></TD>
                    <TD><INPUT TYPE="text" NAME="LastName" SIZE=2Ø></TD>
                    <TD><INPUT TYPE="text" NAME="Age" SIZE=3></TD>
                </TR>
                <TR>
                    <TD><FONT SIZE="-2">First Name</FONT></TD>
                    <TD><FONT SIZE="-2">MI</FONT></TD>
                    <TD><FONT SIZE="-2">Last Name></TD>
                    <TD><FONT SIZE="-2">Age</TD>
                </TR>
                <TR>
                    <TD ROWSPAN="3">How Do We Contact You?</TD>
                    <TD COLSPAN="4" VALIGN="TOP">Street Address: <TEXTAREA 
                    name="StreetAddress" rows=2 cols=3Ø></TEXTAREA></TD>
                </TR>
                <TR>
                    <TD COLSPAN="2">City: <INPUT TYPE="text" NAME="City" 
                    SIZE=2Ø></TD>
                    <TD COLSPAN="2">State: <INPUT TYPE="text" NAME="State" 
                    SIZE=2></TD>
                </TR>
                <TR>
                    <TD COLSPAN="2">ZIP Code: <INPUT TYPE="text" NAME="ZIPCode" 
                    SIZE=1Ø></TD>
                    <TD COLSPAN="2">Daytime Phone
                        (<INPUT TYPE="text" NAME="Phone1" SIZE=3>)
                        <INPUT TYPE="text" NAME="Phone2" SIZE=3>-
                        <INPUT TYPE="text" NAME="Phone3" SIZE=4></TD>
                </TR>
                <TR>
                    <TD>Credit Card
                        <INPUT TYPE="radio" NAME="CreditCardType" VALUE="Visa" 
                        CHECKED>Visa
                        <INPUT TYPE="radio" NAME="CreditCardType" 
                        VALUE="MasterCard">M/C</TD>
                    <TD COLSPAN="2" ALIGN="CENTER">
                        <INPUT TYPE="text" NAME="CreditCardNumber1" SIZE=4>
                        <INPUT TYPE="text" NAME="CreditCardNumber2" SIZE=4>
                        <INPUT TYPE="text" NAME="CreditCardNumber3" SIZE=4>
                        <INPUT TYPE="text" NAME="CreditCardNumber4" SIZE=4></TD>
                    <TD COLSPAN="2">Expiration Date:
                        <INPUT TYPE="text" NAME="ExpirationMonth" SIZE=2>/
                        <INPUT TYPE="text" NAME="ExpirationYear" SIZE=2></TD>
                </TR>
                <TR>
                    <TD>Merchandise</TD>
                    <TD COLSPAN=4><SELECT MULTIPLE NAME="Merchandise" SIZE=1>
                        <OPTION SELECTED VALUE="2ØØØ"> HAL-47Ø <OPTION 
                        VALUE="3ØØØ"> Banana9ØØØ
                        <OPTION VALUE="8ØØ"> High Res Monitor <OPTION VALUE="5Ø"> 
                        Low Res Monitor
                        <OPTION VALUE="25Ø"> Deluxe Keyboard <OPTION VALUE="4Ø"> 
                        Regular Keyboard
                        <OPTION VALUE="2ØØØ"> Laser Printer <OPTION VALUE="6ØØ"> 
                        Inkjet Printer <OPTION VALUE="2ØØ"> Dot Matrix Printer
                        <OPTION VALUE="1ØØ"> Mouse <OPTION VALUE="125"> Trackball
                        <OPTION VALUE="5ØØ"> Scanner
                        </SELECT></TD>
                </TR>
                <TR>
                    <TD ALIGN=CENTER COLSPAN="5">
                        <H1>Thank You For Your Order!</H1>
                    </TD>
                </TR>
            </TABLE>
            <CENTER>
                <INPUT TYPE="button" VALUE="Ship It!" ONCLICK="checkForm()">
                <INPUT TYPE="reset" VALUE="Clear Entries">
            </CENTER>
        </FORM>
    </BODY>
</HTML>

Improving the solution

You can further improve the JavaScript solution. You could perform additional checking for post office box addresses (hint: make a copy of the street address and convert the copy to uppercase), because only the United States Postal Service can deliver to a post office box. You could verify that the state is a valid post office abbreviation (convert it to uppercase). If you're really ambitious, you can try tying the state, zip code, and area code together to see if they work (an address in Georgia with a zip code that starts with 8 and an area code of 919 would be a bad address, for example). A confirmation box announcing what customers have purchased and how much they're about to spend would be a very nice touch. If the customer clicks on CANCEL, no harm done. While you have the customer there, why not scroll additional advertising along the status bar?

Modifying the example for your own use

Although this is a fictitious example, the techniques used here do carry over to real-world applications. In particular, some of the functions used in the Java-Script solution-in particular isDigit() and isAllDigits()-are quite useful for checking numeric output, such as the phone number and credit card number validation in this chapter, or driver's license and social security numbers. The MOD 10 algorithm is in fact used by most major credit cards, and I've tested it against all of my bank cards-it really works.