Programming Articles - JavaScript, XHTML, CSS, Java

Sunday, 17 August 2008

Print

JavaScript Form Validation

Want to validate user input for an (X)HTML form without embedding a single line of JavaScript, without having to worry about violating semantics, and all the while providing a reactive, user friendly experience?

Introducing GValidator - a powerful, extensible yet easy to implement JavaScript library that provides constant feedback during form completion. To use the library all you need to do is include it.

Example form with validation icons

The aim

The aim of GValidator is to provide the following:

The Code

GValidator now has a Google Code project. To get the latest version, post bugs,  comment on or just look about visit: http://code.google.com/p/gvalidator 

Actually, for basic forms no extra coding is needed other than to include the JavaScript file and to add appropriate class names to your form fields. Here is an example form:

<form action="/action/" method="post" class="gform" >
<input type="text" name="firstname" class="firstname" maxlength="30"/>
<input type="text" name="lastname" class="lastname" id="lastname" maxlength="30"/>
<input type="text" name="subject" class="text" id="subject" maxlength="30"/>
<input type="text" name="captcha" class="captcha" id="captcha" maxlength="30"/>
<input type="text" name="phone" class="phone" id="phone" maxlength="12"/>
<input id="submit" type="submit"/>
</form>

There are a few things to notice here;

The Example

Field Types (default XHTML API)

The following are the field types and corresponding class names that are included in the default implementation. They should cover most cases. If you require more than this or customisation, then you can extend the functionality as discussed below.

Form element
Description Class name
text Generic text input. Allows any characters except the following - ().;<> and is not mandatory text
textGeneric required text input. Allows any characters except the following - ().;<> and must be completed
genericrequiredtext
textName field
name
textPhone number field. Allows between 8 - 12 digits only.
phone
textEmail field email
text Captcha field captcha
textRE-Captcha text field. Displays a context and error message related to the RE-CAPTCHA system
recaptcha
textarea
Generic textarea input. Allows any characters except the following - ().;<>. Not require text
select
Generic combo box. Requires the user to select an option with a non-empty value select
radio group
Generic radio group. Requires that the user selects at least one of the options radio
check box
Generic checkbox group. Requires that the user selects at least one of the options checkbox

GValidator Library Design

The library was designed with simplicity and extensibility in mind. The basic idea was to create an abstract class hierarchy that could be quickly and easily extended to change the validation function for a new field type. This hierarchy exists in three tiers allowing for the following object types:

By implementing the library in this way, we allow for simple extensibility by implementing subclasses at the lowest level.

Class Hierarchy

Basic Form Field Class Hierarchy

Customising GValidator

To create your own basic validation functions, you need to do the following things:

  1. Create a class at the first level, overriding the AbstractFormElement of your choice. i.e. To create a UsernameFormField class you would subclass the AbstractTextField class
  2. Set isRequired to true or false and override any other instance variables if necessary
  3. Override the validation function (and any other functions necessary) to do the custom form field validation
  4. Override the getNewInstance method which returns a instance of the new class. This is used by FormFieldFactory to automatically assign your class to a form field.
  5. Register the new class with FormFieldFactory
We will run through these steps in the following example.

Customisation example

Lets say we would like to create a new field type username for validating usernames. The requirements are as follows:

Step 1: Subclass AbstractFormElement

Create a new class ONEGEEK.forms.GenericTextField and subclass AbstractTextField.

ONEGEEK.forms.UsernameTextField = function(field) {}
ONEGEEK.forms.UsernameTextField.prototype = new ONEGEEK.forms.AbstractTextField;

Step 2: Override existing instance variables

In this case, we need to make the field required and we want to validate the field using a regular expression. We can override the instance variables in AbstractTextField to achieve this. Override the following instance variables like so:

this.field = field;
this.name = 'username';
this.isRequired = true;
this.regex = /([a-zA-Z0-9-\'\s]{8,10})/g
this.cleanRegex = /[^0-9a-zA-Z-\'\s]/g
this.isRequired = true;
this.errorMessage = 'Your username must be between 8 and 10 characters';
this.contextMessage = 'You will use this to login. It must be between 8 and 10 characters';

Step 3: Override the validation function

The validation function is where most of the action happens. This function is responsible for checking whether the data has been modified and is valid, and setting the appropriate state of the field (INFO, OK, ERROR). This is where your custom AJAX code would go.

Assuming that an AJAX function called checkUsername(name, field) exists, which takes a username and a ConcreteFormElement object, this is how you would implement the validation function:

this.validate = function() {
this.clean();
this.pattern = new RegExp(this.regex);
var validated = this.pattern.test(this.field.value);

if(validated) {
// Regex validated, now use AJAX to check if username exists
// On successfull AJAX validation set state to OK - formField.setState(FIELD_STATUS_OK);
// On failure of AJAX validation set state to ERROR - formField.setState(FIELD_STATUS_ERROR);
checkUsername(this.field.value, formField);
} else {
if(this.modified == false) {
this.setState(FIELD_STATUS_INFO);
} else {
this.setState(FIELD_STATUS_ERROR);
}
}
return true;
}

Step 4: Override getNewInstance()

This function is simple yet vital, as it is called automatically by FormFieldFactory and assigns the object returned to be the handler for a particular form field. This assigned is done by matching a class name attribute from a form to a registered class name which we do in step 6.

this.getNewInstance = function(field) {
return new ONEGEEK.forms.UsernameTextField(field);
}

Step 5: Register the class with FormFieldFactory

Your custom validation function wil never be used unless you register with FormFieldFactory and tell it which classes you want it to automatically handle. Let's say that you want to use the class "username" with this function as in the example form element below:

<input type="text" name="username" class="username"/>

To get your new function to handle this, you simply do the following:

formFieldFactory.registerFormField('username', new ONEGEEK.forms.UsernameTextField());

Now when you load the page your new function will handle the validation!

Full customisation code

Here is the entire code listing for the customisation:

ONEGEEK.forms.UsernameTextField = function(field) {
this.field = field;
this.name = 'username';
this.isRequired = true;
this.regex = /([a-zA-Z0-9-\'\s]{8,10})/g
this.cleanRegex = /[^0-9a-zA-Z-\'\s]/g
this.isRequired = true;
this.errorMessage = 'Your username must be between 8 and 10 characters';
this.contextMessage = 'You will use this to login. It must be between 8 and 10 characters';

this.getNewInstance = function(field) {
return new ONEGEEK.forms.GenericTextField(field);
}

this.validate = function() {
this.clean();
this.pattern = new RegExp(this.regex);
var validated = this.pattern.test(this.field.value);

if(validated) {
// Regex validated, but use AJAX to check if username exists
// On successfull AJAX validation set state to OK - this.setState(FIELD_STATUS_OK);
// On failure of AJAX validation set state to ERROR - this.setState(FIELD_STATUS_ERROR);
checkUsername(this.field.value, this);
} else {
if(this.modified == false) {
this.setState(FIELD_STATUS_INFO);
} else {
this.setState(FIELD_STATUS_ERROR);
}
}
return true;
}
}

// Subclass Abstract FormField class
ONEGEEK.forms.UsernameTextField.prototype = new ONEGEEK.forms.AbstractTextField;

// Register the field types with FormFieldFactory
formFieldFactory.registerFormField('username', new ONEGEEK.forms.UsernameTextField());

GValidator Browser Support

The library has been tested successfully on the following browsers: