[ Team LiB ] |
8.2 Performing Common Text Field ValidationsNN 2, IE 3 8.2.1 ProblemYou want to verify that a text box contains one of the following: any text, a number, a string of a fixed length, or an email address. 8.2.2 SolutionApply the library of text field validation routines shown in the Discussion (Example 8-1 and Example 8-2) to your form. The library includes the following functions:
For real-time validation of text box entries, use an onchange event handler in the input element and pass a reference to the element by way of the this keyword. For example, the following input element could be used for an email address: <input type="text" size="30" name="eMail" id="eMail" onchange="isEMailAddr(this)" /> See Recipe 8.3 for an example of how these validation functions can be linked together in batch validation prior to submitting the form. The return values from the validation functions are vital for successful operation triggered by the form's onsubmit event handler. 8.2.3 DiscussionExample 8-1 shows a set of fully backward-compatible text validation functions. All of these functions are to be invoked by both the onchange event handler of the text box and a batch validation function triggered by the onsubmit event handler of the enclosing form. All functions are passed references to the form control invoking the event handler. Example 8-1. Backward-compatible text field validation functions// validates that the field value string has one or more characters in it function isNotEmpty(elem) { var str = elem.value; if(str = = null || str.length = = 0) { alert("Please fill in the required field."); return false; } else { return true; } } // validates that the entry is a positive or negative number function isNumber(elem) { var str = elem.value; var oneDecimal = false; var oneChar = 0; // make sure value hasn't cast to a number data type str = str.toString( ); for (var i = 0; i < str.length; i++) { oneChar = str.charAt(i).charCodeAt(0); // OK for minus sign as first character if (oneChar = = 45) { if (i = = 0) { continue; } else { alert("Only the first character may be a minus sign."); return false; } } // OK for one decimal point if (oneChar = = 46) { if (!oneDecimal) { oneDecimal = true; continue; } else { alert("Only one decimal is allowed in a number."); return false; } } // characters outside of 0 through 9 not OK if (oneChar < 48 || oneChar > 57) { alert("Enter only numbers into the field."); return false; } } return true; } // validates that the entry is 16 characters long function isLen16((elem) { var str = elem.value; if (str.length != 16) { alert("Entry does not contain the required 16 characters."); return false; } else { return true; } } // validates that the entry is formatted as an email address function isEMailAddr(elem) { var str = elem.value; str = str.toLowerCase( ); if (str.indexOf("@") > 1) { var addr = str.substring(0, str.indexOf("@")); var domain = str.substring(str.indexOf("@") + 1, str.length); // at least one top level domain required if (domain.indexOf(".") = = -1) { alert("Verify the domain portion of the email address."); return false; } // parse address portion first, character by character for (var i = 0; i < addr.length; i++) { oneChar = addr.charAt(i).charCodeAt(0); // dot or hyphen not allowed in first position; dot in last if ((i = = 0 && (oneChar = = 45 || oneChar = = 46)) || (i = = addr.length - 1 && oneChar = = 46)) { alert("Verify the user name portion of the email address."); return false; } // acceptable characters (- . _ 0-9 a-z) if (oneChar = = 45 || oneChar = = 46 || oneChar = = 95 || (oneChar > 47 && oneChar < 58) || (oneChar > 96 && oneChar < 123)) { continue; } else { alert("Verify the user name portion of the email address."); return false; } } for (i = 0; i < domain.length; i++) { oneChar = domain.charAt(i).charCodeAt(0); if ((i = = 0 && (oneChar = = 45 || oneChar = = 46)) || ((i = = domain.length - 1 || i = = domain.length - 2) && oneChar = = 46)) { alert("Verify the domain portion of the email address."); return false; } if (oneChar = = 45 || oneChar = = 46 || oneChar = = 95 || (oneChar > 47 && oneChar < 58) || (oneChar > 96 && oneChar < 123)) { continue; } else { alert("Verify the domain portion of the email address."); return false; } } return true; } alert("The email address may not be formatted correctly. Please verify."); return false; } Regular expression versions of the validation functions are more compact when the validation is complex (as in the case of email addresses), but they require great care and stress testing to make sure they are doing what you expect. Example 8-2 shows the equivalent validation functions using regular expressions. Example 8-2. Text field validation functions with regular expressions// validates that the field value string has one or more characters in it function isNotEmpty(elem) { var str = elem.value; var re = /.+/; if(!str.match(re)) { alert("Please fill in the required field."); return false; } else { return true; } } //validates that the entry is a positive or negative number function isNumber(elem) { var str = elem.value; var re = /^[-]?\d*\.?\d*$/; str = str.toString( ); if (!str.match(re)) { alert("Enter only numbers into the field."); return false; } return true; } // validates that the entry is 16 characters long when // input field's maxlength attribute is set to 16 function isLen16(elem) { var str = elem.value; var re = /\b.{16}\b/; if (!str.match(re)) { alert("Entry does not contain the required 16 characters."); return false; } else { return true; } } // validates that the entry is formatted as an email address function isEMailAddr(elem) { var str = elem.value; var re = /^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$/; if (!str.match(re)) { alert("Verify the email address format."); return false; } else { return true; } } Notice that the validation done in these functions provides the user with less detailed information about the more complex data entries than the backward-compatible versions. It is possible to provide more information, but this involves pulling apart the regular expressions to test for subsets of matches. In the first one, isNotEmpty( ), the regular expression pattern looks for a string with one or more characters of any kind (.+). To test for a number in isNumber( ), the pattern looks for a string that begins with (^) zero or one minus signs ([-]?), followed by zero or more numerals (\d*), zero or one decimals (\.?), and zero or more numerals (\d*) on the tail end ($). A fixed-length string pattern in isLen16( ) looks for word boundaries on both ends (\b) and any characters (.) appearing 16 times ({16}). To ensure the user keeps to the 16-character length, limit the text-type input element to a maximum length of 16. The gnarled email pattern inside isEMailAddr( ) looks for a match that begins (^) with one or more letters, numerals, underscores, or hyphens ([\w-]), followed by zero or more combinations of a period, letter, numeral, underscore, or hyphen ((\.[\w-]+)*), followed by the @ sign, followed by one or more computer or domains (([\w-]+\.)+), followed by two to seven upper- or lowercase letters for the top-level domain name ([a-zA-Z]{2,7}) on the tail end ($). The pattern does not match the use of straight IP addresses for the portion after the @ sign, but the email message specification (Internet Engineering Task Force RFC 822) frowns on such usage anyway. In addition to individual validation routines, you sometimes need to cascade them. For example, none of the functions that validate numbers, fixed-length strings, or email addresses perform any checking that assures the field has something in it. For example, if the email address field is a required field in the form, you would wire the onchange event handler for that input element to pass the values first to the isNotEmpty( ) function and then the isEMailAddr( ) function—but in such a way that if the first fails, the second one does not execute. That's where the returned Boolean values of the functions come into play: <input type="text" size="30" name="eMail" id="eMail" onchange="if (isNotEmpty(this)) {isEMailAddr(this)}" /> Not shown among the text field validation routines here is one that validates a date entry. Validating date entries is tricky business due to the wide range of date formats and sequences of numbers used around the world. Except for intranet application where everyone standardizes on a single date format, I recommend implementing date input as three distinct fields (or select elements) for entry of month, date, and year. Use the onsubmit event handler to combine the values into a single string for a hidden input element whose value the server can pass directly to the database. You can also use the pop-up calendar shown in Recipe 15.9 to help a user select a date, leaving the formatting up to your scripts. Or, if all of your users follow a fixed date format, you can try the date validation techniques described in Recipe 2.12. 8.2.4 See AlsoRecipe 8.3 for a batch validation structure that uses the functions just described; Recipe 8.4 for automatically focusing and selecting a text field that fails validation; Recipe 2.12 for date validation ideas. |
[ Team LiB ] |