jQuery Form Validation

In this tutorial, we’ll continue developing the HTML5 form that we’ve been working on.  We’re going to import jQuery and the jQuery Validate plugin into our page, so that we can validate any data entered into the form.  In the next part, we’ll work on propping up any HTML5 UI improvements that aren’t supported by the user’s browser using jQuery plugins.

At the bottom of our html contact form, just before the closing body tag, import jQuery from the Google CDN into the page.  Unfortunately, the Google CDN doesn’t currently host the jQuery validate plugin, but Microsoft do, so we’ll import it from their CDN.  After this, import a new script – I’ve called mine ‘global.js’ – into the page.  Don’t forget to create the new empty script file.

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script src="http://ajax.microsoft.com/ajax/jquery.validate/1.7/jquery.validate.min.js"></script>
<script src="global.js"></script>

A lot of jQuery code samples you see start a script with the document.ready function.  There isn’t any need for us to do that here, as we’ve put our script tags at the bottom of the page – the document has already loaded before we’ve told the browser to fetch any scripts.

We’re going to check the data entered against the following patterns:

Fieldset 1

  • Name: letters, hyphens and spaces only, required field
  • Company: no validation
  • Email: email format (a@b.c), required field
  • Telephone: numbers only, min 11 chars, max 11 chars, required field
  • Website: url format

Fieldset 2

  • Type of Project: no validation (one item is selected by default), dynamically generate a textarea to collect details
  • Estimated Completion Date: no validation (we’ll use a jQuery datepicker plugin here later)
  • Estimated Budget: required field, numbers only
  • Additional Details: no validation

Fieldset 3

  • Please contact me by… : no validation required, as an option is selected by default
  • Best time to contact: disabled unless ‘contact by phone’ is selected

If all the tests pass, the form will be submitted; otherwise, the user will get instant feedback regarding any errors.  In global.js, write the following code:

$('form').validate();

If you specify which fields to validate in the html itself (usually by adding class=”required” to the input field you want to validate), this would initialise the plugin with the default options.  However, we want some custom validation and custom error messages, so we’re going to have to delve a little more deeply into the plugin’s interface.  For reference, all the methods associated with the plugin can be found here.

Options are specified by passing them into the validate() function with an object literal, as follows:

$('form').validate({
    rules : {}
});

This is where we will define the validation rules for each of our form inputs.  Each form input is referred to by it’s ‘name’ attribute.

$('form').validate({
    rules : {
        name : {},
        email : {},
        telephone : {},
        website : {},
        'project-budget' : {},
        'contact-time' : {}
    }
});

Notice that we’ve had to wrap any keys containing a hyphen in quotes, as hyphens are not permitted in property names.  Also, be sure to omit the comma for the last item of an object literal, as this will throw an error in IE.  Lets go through the form and add a ‘required’ rule for all the appropriate fields.

$('form').validate({
    rules : {
        name : {
            required : true
        },
        company : {},
        email :  {
            required : true
        },
        telephone :  {
            required : true
        },
        website : {},
        'project-budget' :  {
            required : true
        },
        'contact-time' : {}
    }
});

If we were to leave a field empty now, we’d see the default error message, telling us that the field is required.

Lets fill out the rest of the options.

$('form').validate({
    rules : {
        name : {
            required : true,
            letters : true
        },
        email :  {
            required : true,
            email : true
        },
        telephone :  {
            required : true,
            digits : true,
            minlength : 11,
            maxlength : 11
        },
        website : {
            url : true
        },
        'project-budget' :  {
            required : true,
            digits : true
        },
        'contact-time' : {
            required : function(element) {
                return $('.contact-method input:last').is(':checked');
            }
        }
    }
});

The above code is pretty self-explanatory, but for a couple of points.  Firstly, ‘required’ is not set to true or false on the ‘contact-time’ field, as we want the field to be a required field only if the ‘phone’ option is selected.  We need to pass the ‘required’ method a function containing an expression to be evaluated; this value is then set to be the return value of the function.  If the ‘phone’ option is selected, the expression will evaluate to true, so the field will be required, otherwise, the expression will be false, and the field will not be required.

Secondly, the ‘letters’ method is not a method native to the plugin, so we have to create a custom method using $.validator.addMethod.  We want to create a method that will check the data to match a pattern of letters, spaces and hyphens only.  We do this by matching the data against a regular expression.  Follow the code below as we build up a regular expression for matching lettters:

  1. [a-z] – lowercase letters only
  2. [a-zA-Z] – lowercase and capitals
  3. [- a-zA-Z] – lowercase, capitals, hyphen, space
  4. ^[- a-zA-Z]$ – ^marks the beginning of a pattern, $ marks the end
  5. ^[- a-zA-Z]+$ – the plus(+) sign after the brackets but before the end of the pattern denotes that we want to find more than 1 character
  6. /^[- a-zA-Z]+$/ – we wrap the enter regular expression in the JavaScript regex literal (//)

Lastly, we use the match method to check if the field data is a match:

6.   value.match(/^[- a-zA-Z]+$/);

The final code for the custom letters method is as follows:

$.validator.addMethod('letters', function(value) {
    return value.match(/^[- a-zA-Z]+$/);
});

Next we need to specify the error messages that we want to be displayed for each test, as below.

$('form').validate({
    rules : {
        name : {
            required : true,
            letters : true
        },
        email :  {
            required : true,
            email : true
        },
        telephone :  {
            required : true,
            digits : true,
            minlength : 11,
            maxlength : 11
        },
        website : {
            url : true
        },
        'project-budget' :  {
            required : true,
            digits : true
        },
        'contact-time' : {
            required : function(element) {
                return $('.contact-method input:last').is(':checked');
            }
        }
    },
    messages : {
        name : {
            required : 'The name field cannot be blank',
            letters : 'Please enter letters only'
        },
        email :  {
            required : 'The email field cannot be blank',
            email : 'Please enter a valid email address'
        },
        telephone :  {
            required : 'The telephone field cannot be blank',
            digits : 'Please enter numbers only',
            minlength : 'Please enter a UK phone number (11 digits)',
            maxlength : 'Please enter a UK phone number (11 digits)'
        },
        website : {
            url : 'Please enter a valid url'
        },
        'project-budget' :  {
            required : 'The project budget field cannot be blank',
            digits : 'Please enter numbers only'
        },
        'contact-time' : {
            required : 'The contact time field cannot be blank'
        }
    }
});

That’s all the validation we’re going to do in this part of the tutorial, in the next section we’ll improve the UI with widgets in browsers that don’t support the native HTML5 form improvements yet. Before we finish, we’ll just tidy up those error messages.

From the screenshot above, we can see that the errors are displaying in the wrong place – they should be underneath the error field, not to the bottom left where the field labels are.  To remedy this, we simply need to override the default css for a label element.  Fortunately, errors generated by the plugin have the class “error”, so we can simply target this class:

label.error { text-align: left; margin-left: 200px; color: #CC0000; padding: 0; height: auto; width: 208px; font-size: 11px }
input.error { border: 2px solid #CC0000 }

View the Demo