Tuesday, May 06, 2008

Keystroke and Field Validation with JavaScript

Yesterday, I provided a simple tool to demonstrate the differences between keydown and keypress events. Today, I'm going to apply that knowledge toward making a lightweight form validation library. This library will prevent invalid characters from being entered in form input elements which is useful toward preventing malicious data from being entered (think XSS), as well as ensuring data integrity (data is in the valid format, all required fields present, etc.).

Sample Form:



Alpha
Alphanumeric*
Date
Decimal
Digit
Email
Hostname
IP address
Integer
Punctuation
Real number
Time
Whitespace
custom
type
format


* denotes required field.




The form above should only allow valid characters in each input field - this is your keystroke validation. Furthermore, for fields that require a specific format, when you leave a field with invalid formatted data it should provide an immediate visual indication of the error by setting a fieldWithErrors CSS class on invalid fields. Additionally, a fv:onInvalid event is fired on the invalid element to allow custom events handlers to provide additional actions. For example, enter a number in the date field and tab out. The field will be outlined in red from the fieldWithErrors CSS class and the message, "Date must be yyyy-mm-dd" will be shown by the handling of the fv:onInvalid event. Note: the CSS class will be removed automatically when the field is corrected, and a corresponding fv:onValid event will be fired to allow the custom code to clean up after itself.

Validation rules are defined as Regular Expressions. If you are not familiar with Regular Expressions here is a good reference. Additionally, many predefined regex rules for various validations can be found at the regex library.

Several common data types and formats are defined by default in the library, but it also provide the mechanism to add you own data types. In the custom entry, the type entry allows you to specify an on-the-fly keystroke validation regular expression, and the format entry allows you to specify an on-the-fly field format validation regular expression.

The Validate Form button demonstrates the action you would want to perform before a form submission. It invokes a validateAllFields function which just runs all the field validations (and invokes corresponding CSS rules and issues events). It also returns an array of the invalid field names in case you want to build a list of errors (like the Rails flash messages).


Usage Instructions

Sample Usage:

<script src="javascript/formValidation.js"></script>
<script>
var vf = new FormValidation();
vf.addValidationForField("date", "date", "date");
</script>

addValidationForField requires 3 parameters:

  1. The identifer of the field to add validation for

  2. The name of the data type to use for keystroke validation

  3. The name of the data format to use for field validation

In this sample all the names are the same, but they usually wouldn't be.

Just doing the above will prevent invalid characters from being entered in the data field and can indicate when the field is invalid.

When a field is changed, the format validation is run and will do two things.

  1. It will mark invalid fields are the fieldWithErrors CSS class. This can be used to appropriately highlight the error to the user. For example, the following CSS rule will outline the field in red:

    .fieldWithErrors {
    border: 3px solid red;
    }

  2. An event will also be issued in the field to indicate whether or not the field is valid. The events to watch for are fv:onInvalid and fv:onValid. These events can be used to hide/show error messages such the following which will display a pre-defined error message:

    $$('input').each(function(inputElem) {
    inputElem.observe("fv:onInvalid", function() {
    $(this.identify() + "-error").show();}.bind(inputElem));
    inputElem.observe("fv:onValid", function() {
    $(this.identify() + "-error").hide();}.bind(inputElem));
    });

    Or any number of other actions. For example, you may wish to display a popup error message such as those provided by Prototype Window or add messages to an error flash area as commonly done in Rails apps or toggle the submit button as enabled/disabled. Using these events allows you to add whatever actions you desire when the
    validation fails (or passes).



Download
formValidation.js

8 comments:

Ricky said...

Hi Steven, this validation method is amazing.. but i cant figure out how to use it..

called the formValidation.js file on my HEAD section...

add this:
vf.addValidationForField("email", "email", "email");

added the fieldWithErrors class to my CSS file...

on my form i have an input field with the id 'email'..

AM I MISSING SOMETHING?
SHOULD THIS WORK LIKE THIS???

sorry for bother..

Steven Pothoven said...

First, I would recommend placing the javascript at the end of the BODY section for both performance reasons (see http://developer.yahoo.com/performance/rules.html#js_bottom) and because the call to addValidationForField is going to be looking for the element in the HTML so if it hasn't loaded yet, it won't find it.

Second, I don't think I said it in the blog post, but I'm pretty sure for formValidation.js depends on prototype.js so be sure to include that too. I recommend using Google's hosted copy at http://ajax.googleapis.com/ajax/libs/prototype/1.6.0.2/prototype.js to both save bandwidth on your site, but also to take advantage of it being pre-cached in your browser from someone else's web page.

BAC said...

recommend that a server side validation script is also implemented in case JavaScript is turn off in the browser.

Steven Pothoven said...

Yes, you should never depend on the client to be safe.

Cosmo Scrivanich said...

This is outstanding work and your isEditKey function saved the day for me. I'm writing similar JavaScript and was very stuck on how to eliminate edit keys. Although, I'm not exactly sure what these lines are intended for:

if (!Prototype.Browser.IE && !Prototype.Browser.WebKit) {
var _f = Prototype.Browser.KHTML ? 127 : 46;

Steven Pothoven said...

Cosmo,
I believe the code you're referring to is:

// IE and Safari do not issue the keypress events for edit kets which utilize this function,
// however, some regular keys will be marked as edit keys if we allow them to run this
// code in regular keystrokes (ex. '-' = 45 and '.' = 46 in Safari).
//
if ( !Prototype.Browser.IE &&
!Prototype.Browser.WebKit ) {

var deleteKey = Prototype.Browser.KHTML ? 127 : 46;
switch (keyCode) {
case 8: // backspace
case 9: // tab
case 33: // page-up
case 34: // page-down
case 35: // end
case 36: // home
case 37: // left-arrow
case 38: // up-arrow
case 39: // right-arrow
case 40: // down-arrow
case 45: // insert
case deleteKey:
isEdit = true;
break;
...


So, this switch statement will only come into play for non-IE or WebKit (Safari) browsers, like Firefox. For most of these, the key code of 46 would be the delete key. However, for KHTML (Konqueror) browsers the delete key code is 127, so the 'deleteKey' variable is set 127 for KHTML or 46 otherwise to check for the delete key.

Cosmo Scrivanich said...

That did the trick Steven. Created the code for JQuery:

if ( !$.browser.msie && !$.browser.webkit ) {

Don't have to support Konqueror so I only have a single case for the delete key. Thanks for your help!!!

Steven Pothoven said...

Glad I could help!

Since the time of this post, I've migrated over to jQuery primarily myself. If you have a jQuery version of this code, I'd be happy to add it here.