Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Beginning JavaScript With DOM Scripting And Ajax - From Novice To Professional (2006)

.pdf
Скачиваний:
81
Добавлен:
17.08.2013
Размер:
17.27 Mб
Скачать

346

C H A P T E R 9 D A T A V A L I D A T I O N T E C H N I Q U E S

The biggest mistake a lot of client-side validation tutorials and ready-made solutions make is not considering localization.

My birthday in American notation is 4/26/1975, in Europe 26/04/1975; others might write it as 26. April 1975 or Apr. 26, 1975.

Numbers are displayed as 1,000.95 in England and America, whereas Germans write and enter 1.000,95.

Americans and the English write 1pm or 1am, whereas other Europeans go for 13.00 or 1.00.

English post codes have syntax like N16 5UN, American ZIP codes 12345 and an optional -1234, whereas in Germany the post codes used to be four digits before the reunion and are now five digits.

Testing whether telephone numbers are only numerical is not enough, as people might provide the optional country codes or extension numbers, like +44 (0)208 11111-1122.

Interestingly, enough back-end solutions and frameworks like .NET, Spring, and Mono have these issues sorted out for you already, and you can define your displays and validation rules in localization files. This means that if you work in an environment that uses these, it might be a good idea to share validation rules with the back end. We will come back to this in the “Sharing Validation Rules” section.

Basic JavaScript Validation with String and Numeric Methods

The easiest form of validating user entry is using string and numeric methods. I’ve covered a lot of these in Chapter 2 when I talked about data types and conditions, but I’ll recap them here.

String Validation Methods

The string methods most useful for validation are the following:

charAt(n): Returns the character at the nth position in the string, starting at 0.

charCodeAt(n): Returns the Latin-1 ASCII character value at the nth position in the string starting at 0.

indexOf(search): Returns the position of search inside the main string. Returns -1 if the search cannot be found.

lastIndexOf(search): Returns the last position of search inside the main string. Returns -1 if the search cannot be found.

slice(start,end): Returns the part of the string in between start and end. If you don’t provide an end value, it returns the rest of the string.

split(search): Splits the string into an array with the parts of the main string surrounding search as the elements. The search term itself is not part of this array.

C H A P T E R 9 D A T A V A L I D A T I O N T E C H N I Q U E S

347

substr(start,n): Returns the string that is n characters long beginning at start.

substring(start,end): Returns the part of the string in between start and end.

The other, more powerful, string methods, search, replace, and match, work with regular expressions. I will talk about those shortly.

You can use the string methods, for example, to check whether a value is a valid number:

exampleCheckNumberString.html (excerpts)

<form onsubmit="return isNumber()"> <p>

<label for="total">Total</label>

<input type="text" name="total" id="total" /> </p>

<p><input type="submit" value="send" /></p> </form>

function isNumber() { var currentCode;

var total = document.getElementById( 'total' ); if( !total ) { return false; }

total = total.value;

if( total.length == 0 ) { alert( 'Field is empty' ); return false;

}

if( total.indexOf( '-' ) != -1 && total.substring( 0,1 ) != '-' || total.lastIndexOf( '-' ) != total.indexOf( '-' ) ) {

alert( 'A number can only have a minus at the beginning' ); return false;

}

if( total.indexOf( '.' ) != -1 &&

total.lastIndexOf( '.' ) != total.indexOf( '.' ) ) { alert( 'A number can only have one decimal point' ); return false;

}

for( var i = 0; i < total.length; i++ ) { currentCode = total.charCodeAt( i );

if( currentCode != 45 && currentCode !=46 && currentCode < 48 || currentCode > 57 ) {

alert( 'Only Numbers are allowed' ); return false;

}

}

return true;

}

348

C H A P T E R 9 D A T A V A L I D A T I O N T E C H N I Q U E S

Let’s go through the script and see what is going on there.

exampleCheckNumberString.html

function isNumber () { var currentCode;

var total = document.getElementById( 'total' ); if( !total ){ return false; }

total = total.value;

You define a variable called currentCode and get the value of the form element with the ID total.

exampleCheckNumberString.html (continued)

if( total.length == 0 ) { alert( 'Field is empty' ); return false;

}

Next, you test whether a value was entered for the field by verifying that the length of the value is not 0; if it is, you stop the form submission by returning false after displaying an error message.

exampleCheckNumberString.html (continued)

if( total.indexOf( '-' ) != -1 && total.substring( 0,1 ) != '-' || total.lastIndexOf( '-' ) != total.indexOf( '-' ) ) {

alert( 'A number can only have a minus at the beginning' ); return false;

}

As valid numbers should only have a minus as the first nondigit character (theoretically, it could also be a + or a ., but who would enter that?), you test whether the value contains a minus (indexOf() does not return -1) and whether it is the first character using substring(). This is not enough though, as the user might have entered -12-12, which means you must also check whether the lastIndexOf() is different from indexOf(). This can only be the case when there is more than one minus sign. If there is more than one minus or it is not at the beginning, you show an error and return false.

exampleCheckNumberString.html (continued)

if( total.indexOf( '.' ) != -1 &&

total.lastIndexOf( '.' ) != total.indexOf( '.' ) ) { alert( 'A number can only have one decimal point' ); return false;

}

The same logic applies to the decimal point; there can be only one in a valid number.

C H A P T E R 9 D A T A V A L I D A T I O N T E C H N I Q U E S

349

exampleCheckNumberString.html (continued)

for( var i = 0; i < total.length; i++ ) { currentCode = total.charCodeAt( i );

if( currentCode != 45 && currentCode != 46 && currentCode < 48 || currentCode > 57 ) {

alert( 'Only Numbers are allowed' ); return false;

}

}

Use the charCodeAt() method to test whether there are any invalid characters in the value. Only minus signs (45), decimal points (46), or the number characters 0 to 9 (48 to 57) are allowed. Loop through the string one character at a time and if there is any character whose ASCII code is outside this range, display an error message and return false;.

exampleCheckNumberString.html (continued)

return true;

}

If all went well, you return true, which sends the form data to the server.

A more complex example is to test for valid e-mail syntax. An e-mail must contain only one @, and the preceding user name part may not start with a period or a hyphen and may not end with a period. The domain part after the @ may not start or end with a period or hyphen. Both parts may only contain the characters from a to z, 0 to 9, periods, underscores, and hyphens.

exampleCheckEmailString.html

function isEmail() {

var mail = document.getElementById( 'email' ); var error = '';

if( !mail ){ return false; } var mailstring = mail.value;

You start by checking for the necessary form field and retrieving its value. Define a variable called error to store error messages. In this example, you need this only to constrain line length, but in the form example at the end of the chapter, we will come back to this.

exampleCheckEmailString.html (continued)

if( mailstring.length == 0 ) {

error = 'You didn\'t enter a value'; alert( error );

return false;

}

Test whether the user entered an e-mail by comparing the value length with 0; display an alert and cancel the form submission if there is nothing in the field.

350 C H A P T E R 9 D A T A V A L I D A T I O N T E C H N I Q U E S

exampleCheckEmailString.html (continued)

if( mailstring.indexOf( '@' ) == -1 ) { error = 'This email has no @ sign'; alert( error );

return false;

} else if( mailstring.lastIndexOf( '@' ) != mailstring. indexOf( '@' ) ) {

error = 'An email may only contain one @ sign.'; alert( error );

return false;

}

Test whether the e-mail contains an @ sign using indexOf() and that it only has one of these by comparing the values returned by indexOf() and lastIndexOf().

exampleCheckEmailString.html (continued)

var chunks = mailstring.split( '@' ); var n = chunks[0];

Split the e-mail string at the @ sign to retrieve the user name and the domain parts to check them separately. You define n as the first “chunk,” which is the user name.

exampleCheckEmailString.html (continued)

if( n.substring( 0,1 ) == '.' || n.substring( 0,1 ) == '-' || n.substr( n.length-1, 1 ) == '.' ) {

error = 'The user name may not start with a period or hyphen'; error += ' and may not end with a period';

alert(error); return false;

}

As the user name may not start with a period or a hyphen, nor end with a period, you test these conditions via substring() and substr().

Note This shows that using substr() is the shorter option when testing for characters at the end of the string. To test for the last character, use string.substr( string.length-1, 1 ) and not string.substring( string.length-1, string.length ).

C H A P T E R 9 D A T A V A L I D A T I O N T E C H N I Q U E S

351

Display an error and stop the form from submitting if the email is invalid.

exampleCheckEmailString.html (continued)

if( !checkValidCharacters( n ) ) {

error = 'The email name contains invalid characters'; alert( error );

return false;

}

The name may only contain numbers, letters, dashes, underscores, and periods. The utility function checkValidCharacters() takes care of validating this and returns true or false accordingly. If there are invalid characters in the user name, you advise the user of this and cancel the form submission.

exampleCheckEmailString.html (continued)

n = chunks[1];

if( n.substring( 0, 1 ) == '.' || n.substring( 0, 1 ) == '-' || n.substr( n.length-1, 1 ) == '-' ||

n.substr( n.length-1, 1 ) == '.' ) {

error = 'The domain name may not start or end with a hyphen or period';

alert( error ); return false;

}

Then take the second “chunk,” which is the domain part of the e-mail, and make sure that neither the start nor the end of the string is a hyphen or a period. Otherwise, alert the user and cancel the form submission.

exampleCheckEmailString.html (continued)

if( !checkValidCharacters( n ) ) {

error = 'The domain name contains invalid characters'; alert( error );

return false;

}

return true;

}

Test the domain for invalid characters and return true—thus sending the form data to the server—if everything is OK.

352 C H A P T E R 9 D A T A V A L I D A T I O N T E C H N I Q U E S

exampleCheckEmailString.html (continued)

function checkValidCharacters( n ) { for( var i = 0; i < n.length; i++ ) {

currentCode = n.charCodeAt( i );

if( currentCode == 45 || currentCode == 46 || currentCode == 95 ||

( currentCode > 96 && currentCode < 123 ) || ( currentCode > 47 && currentCode < 58 ) ) {

continue;

}else {

return false;

}

}

return true;

}

In the utility function checkValidCharacters(), you loop through the entire length of the string (sent as the parameter n) and make sure that the charCode of each of the characters in the string does not fall outside the range of permitted characters. The function returns false if the character is neither a hyphen (45) nor a period (46) nor an underscore (96) nor any character from a to z (97 to 122) nor any character from 0 to 9 (48 to 57). Otherwise, it returns true.

The number checking example seems a bit convoluted, and indeed it is. For numeric validation and comparison, you are normally a lot better off using numeric validation methods.

Numeric Validation Methods

Number(): Tries to convert the value of the variable inside the parentheses into a number.

isNaN( n ): Tests whether n is a number (float or integer) and returns true if it isn’t.

parseFloat( n ): Tries to convert n to a floating point number. It parses the string character by character from left to right, until it encounters a character that cannot be used in a number. It then stops at that point and evaluates this string as a number. If the first character cannot be used in a number, the result is NaN - Not A Number.

parseInt( n ): Converts n to an integer by removing any fractional part without rounding the number up or down. Any nonnumeric characters passed to the function will be discarded. If the first character is not +, –, or a digit, the result is NaN.

Whenever the validation in question is a mathematical one, like testing for a real number, the mathematical methods are much easier to use. The isNumber() function mentioned earlier is a lot simpler if you rewrite it to use isNaN(), which is a method that returns true when its parameter is not a number:

C H A P T E R 9 D A T A V A L I D A T I O N T E C H N I Q U E S

353

exampleCheckNumberMath.html (excerpt)

function isNumber() {

var total = document.getElementById( 'total' ); if( !total ){ return false; }

total = total.value;

if( total.length == 0 ) { alert( 'Field is empty' ); return false;

}

if( isNaN( total ) ) {

alert( 'Please enter a number' ); return false;

}

return true;

}

A more complex example is to check whether a date is the right format. The big problem with dates is that they don’t follow a straight algorithm; things would be a lot easier if there were 10 days to a month, 10 months in a year, and so on. As it stands, you need to check for a lot of details:

Is the month between 1 and 12?

Does the day exist in the month—for example, 31.04.2000? (It doesn’t.)

Is the year a leap year, which makes the 29th of February possible? Let’s go through an example step by step:

exampleCheckDateMath.html (excerpt)

function isValidDate() { var leap;

var datestring = document.getElementById( 'date' ); if( !datestring ){ return false; }

datestring = datestring.value; if( datestring.length == 0 ) { alert( 'Field is empty' );

return false;

}

You start with predefining a variable for leap years, reading the value of the date field, and check whether there is any content in the field.

354

C H A P T E R 9 D A T A V A L I D A T I O N T E C H N I Q U E S

exampleCheckDateMath.html (continued)

if( datestring.length != 10 && datestring.indexOf( '/' )== -1 || datestring.indexOf( '/' ) != 2 ||

datestring.lastIndexOf( '/' ) != 5){

alert( 'The date is not in the format dd/mm/yyyy' ); return false;

}

You continue to check whether the date is in the right format by checking the string length and ensuring that the slashes are at the right positions.

exampleCheckDateMath.html (continued)

var chunks = datestring.split( '/' ); var day = chunks[0];

var month = chunks[1]; var year = chunks[2];

You then split the date into day, month, and year to validate them separately.

exampleCheckDateMath.html (continued)

if( ( month < 1 ) || ( month > 12 ) ) {

alert( 'The month must be in between 01 and 12' ); return false;

}

The month is pretty easy; all you need to test is whether it is larger than 1 and smaller than 12.

exampleCheckDateMath.html (continued)

if( day < 1 ) {

alert('The day cannot be negative'); return false;

}

The first day check is to weed out negative days, which could have slipped through the earlier formatting test.

C H A P T E R 9 D A T A V A L I D A T I O N T E C H N I Q U E S

355

exampleCheckDateMath.html (continued)

 

if( (year % 4 == 0) || (year % 100

== 0) || (year % 400 == 0) ) {

leap = 1;

 

}

 

if( (month == 2) && (leap == 1) &&

(day > 29) ) {

alert( 'This month does not have

that many days' );

return false;

 

}

 

if( (month == 2) && (leap != 1) &&

(day > 28) ) {

alert( 'This month does not have

that many days' );

return false;

 

}

 

For February, you need to check whether the date is a leap year or not. Leap years are those that can be divided by 4, 100, or 400 without resulting in a floating point number. Therefore, you check the modulo (%) of these calculations and compare it with 0. If the year is a leap year, February can have 29 days; otherwise it can only have 28.

exampleCheckDateMath.html (continued)

 

 

if( ( day > 31 ) && ( ( month == "01" )

|| (

month == "03" ) ||

( month == "05" ) || ( month == "07"

) ||

( month == "08" ) ||

( month == "10" ) || ( month == "12"

) ) ) {

alert( 'This month does not have that

many

days' );

return false;

 

 

}

 

 

if( ( day > 30 ) && ( ( month == "04" )

|| (

month == "06" ) ||

(month == "09" ) || ( month == "11" ) ) )

{

alert( 'This month does not have that

many

days' );

return false;

 

 

}

 

 

return true;

 

 

}

 

 

For the rest of the months, you need to determine whether the day entered is over 30 or 31 and whether the month has that many days. As JavaScript has no knuckles to count on, you need to hard-wire these comparisons. If all goes well, you return true to send the form off to the server.

It seems cumbersome to have to hard-wire all these comparisons, and indeed it is. If you remember, JavaScript has a Date object that has all this logic already in it. Therefore, you can harness this to make the validation of a form a lot shorter: