Click here to Skip to main content
Click here to Skip to main content

How can we adapt our applications to use the jQuery Validation plug-in

, 11 Jun 2011 CPOL
Rate this:
Please Sign up or sign in to vote.
This article gives the reader an idea of how the jQuery Validation plug-in works internally and also how can we extend the validation rules from our applications.

Introduction

I was going through the jQuery validation plug-in code (Jquery.validate.js) and found it is very interesting and useful. I thought it would be beneficial if I share my findings here to those who are looking to have an understanding on how the jQuery validation framework works. This article contains the internals of how the jQuery validation plug-in works, and also explains how we can extend the default rules to provide our own validation methods.

Background

I was a bit curious about how jQuery magically validates input controls just by applying simple class rules like required email, required url etc., to a class attribute. So I spent a couple of hours to understand how Jqquery.validate.js was written by experts.

Using the code

To examine how the jQuery validation works, I took a simple HTML page and added a few controls on it. I also applied a few (existing) rules to see how the validation worked. The code below shows the validation.html code.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
                    "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
  <script src="jquery-1.4.1.js"></script>
  <script type="text/javascript" src="jquery.validate.js"></script>
<style type="text/css">
* { font-family: Verdana; font-size: 96%; }
label { width: 10em; float: left; }
label.error { float: none; color: red; padding-left: .5em; vertical-align: top; }
p { clear: both; }
.submit { margin-left: 12em; }
em { font-weight: bold; padding-right: 1em; vertical-align: top; }
.class1 {color:red}
</style>
  <script>
      $.extend($.validator, { cool: function () { } });
      $(document).ready(function () {
          $("#commentForm").validate();
          $.validator.addMethod("PinCode", function () { }, 
                                "PIN code is not in valid format");
      });
  </script>
  
</head>
<body>
  
<form class="cmxform" id="commentForm" method="get" action="">
<fieldset>
   <legend>A simple comment form with submit validation and default messages</legend>
   <p>
     <label for="cname">Name</label>
     <em>*</em><input id="cname" name="name" 
          size="25" class="class1 required"  minlength="2" />
   </p>
   <p>
     <label for="cemail">E-Mail</label>
     <em>*</em><input id="cemail" name="email" 
          size="25"  class="required email" />
   </p>
   <p>
     <label for="curl">URL</label>
     <em>  </em><input id="curl" name="url" 
         size="25"  class="required url" value="" />
   </p>
   <p>
     <label for="ccomment">Your comment</label>
     <em>*</em><textarea id="ccomment" name="comment" 
         cols="22"  class="required"></textarea>
   </p>
   <p>
     <input class="submit" type="submit" value="Submit"/>
   </p>
 </fieldset>
 </form>
</body>
</html>

How it works

In the above HTML file, the classes applied were required, required email, required url, etc. These minimal changes on the input elements should be good enough to make them part of the jQuery validation plug-in framework.

The heart of the jQuery validation framework is the jQuery validator ($.validator) which contains all the necessary default settings to handle an element's validation work. the jQuery validator comes with default settings and when we call the validate() method on the form, the validate() method takes options as an input parameter and returns the validator. We can always send our own options which will extend the jQuery default validation settings. Below are the default settings with which the validator comes with initially.

Here are the default settings for the validtor:

messages: {},
groups: {},
rules: {},
errorClass: "error",
validClass: "valid",
errorElement: "label",
focusInvalid: true,
errorContainer: $( [] ),
errorLabelContainer: $( [] ),
onsubmit: true,
ignore: [],
ignoreTitle: false,
onfocusin: function(element) {
    this.lastActive = element;
        
    // hide error label and remove error class on focus if enabled
    if ( this.settings.focusCleanup && !this.blockFocusCleanup ) {
        this.settings.unhighlight && this.settings.unhighlight.call( 
              this, element, this.settings.errorClass, this.settings.validClass );
        this.errorsFor(element).hide();
    }
},
onfocusout: function(element) {
    if ( !this.checkable(element) && 
            (element.name in this.submitted || !this.optional(element)) ) {
        this.element(element);
    }
},
onkeyup: function(element) {
    if ( element.name in this.submitted || element == this.lastElement ) {
        this.element(element);
    }
},
onclick: function(element) {
    // click on selects, radiobuttons and checkboxes
    if ( element.name in this.submitted )
        this.element(element);
    // or option elements, check parent select in that case
    else if (element.parentNode.name in this.submitted)
        this.element(element.parentNode)
},
highlight: function( element, errorClass, validClass ) {
    $(element).addClass(errorClass).removeClass(validClass);
},
unhighlight: function( element, errorClass, validClass ) {
    $(element).removeClass(errorClass).addClass(validClass);
}

We can override any of these settings on our local page. For example, take the event onfocusout; I want to perform a different logic other than what the framework provides. In our local page, we can do some thing like below:

$.validator.defaults.onfocusout = function(element)
{
    //your custom code,
},

The label will be used to display error messages; 'error' has its class, and we can override this class in our application and provide our own custom style, somewhat similar to what we have done in the above sample.

label { width: 10em; float: left; }
label.error { float: none; color: red; padding-left: .5em; vertical-align: top; }

How rules are applied and how the validation takes place

The jQuery validator has a rules object ($.validator.settings.rules). A rule in jQuery associates an element to a validation method, and a method defines the validation process of how an element needs to be validated. The framework rules are categorized into four levels:

  • class level
  • attribute level
  • meta rules
  • static rules

For example, here are some of the classRuleSettings defined in the framework:

classRuleSettings: {
    required: {required: true},
    email: {email: true},
    url: {url: true},
    date: {date: true},
    dateISO: {dateISO: true},
    dateDE: {dateDE: true},
    number: {number: true},
    numberDE: {numberDE: true},
    digits: {digits: true},
    creditcard: {creditcard: true}
},

Rules preparation for an element

To get the rules applied on an element, jQuery first gets the value of the class attribute; for example, consider our sample email element:

  1. It has "required email" as class value
  2. It splits the value returned by the class attribute using the delimiter ' '; in our case, we get two values: required, email.
  3. For each value (required, email), it gets the mapping from classRuleSettings and prepares the validation rule for the element. Here the validation rule is {required:true,email:true}. For each value in the rule like required, email etc., the framework has methods defined in $.validator.Methods. A list of methods describes how an element needs to be validated based on the rules assigned to it.
  4. Here are the some of the framework validator methods: MinLength, MaxLength, rangelength, min, max, range, url, date, dateISO, number, digits, creditcard, etc.

  5. After class level rules are created, it iterates through other attributes of the input element (like minlength, maxlength) etc., and prepares rule objects like above {required:true, minlength=2} and combines both the rules. It works in a similar way for meta, static rules also.
  6. Once the rules preparation is done, it goes through the validation methods ($.validator.Methods) list and calls the appropriate method based on the rule association. In the above case, three validation methods will be called: required, email, minlength, (<input name="cname" id="cname" class="required email" minlength=2 />). Here is how the code for the methods look like in the validation framework:
  7. $.validator.Methods{
    
      required: function(value, element, param) {
            // check if dependency is met
            if ( !this.depend(param, element) )
                return "dependency-mismatch";
            switch( element.nodeName.toLowerCase() ) {
            case 'select':
                // could be an array for select-multiple or a string,
                // both are fine this way
                var val = $(element).val();
                return val && val.length > 0;
            case 'input':
                if ( this.checkable(element) )
                    return this.getLength(value, element) > 0;
            default:
                return $.trim(value).length > 0;
            }
        },
      email: function(value, element) {
            // contributed by Scott Gonzalez:
            //   http://projects.scottsplayground.com/email_address_validation/
            return this.optional(element) || /^((([a-z]|\d|[!#\$%&'\*\+\-
                   \/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+
                   (\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\u
                   FDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+
                   )?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|
                   [\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x
                   7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x
                   0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-
                   \uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-
                   \uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\u
                   FFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+((
                   [a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\u
                   D7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\u
                   F900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\u
                   FDCF\uFDF0-\uFFEF])))\.?$/i.test(value);
        },
    }

What if the framework provided validation methods are not sufficient

How do we add our own validation mechanisms/methods? Well, the framework provides a method called $.validator.addMethod(name, method, Message) which adds a new validation method to the Methods list. Here, name is the name of the validation method, method is the validation process, and Message is the validation failure message. In the above sample, I have added a pin code validation method which is not part of the framework. For simplicity, the method always returns false to kick the validation, and the validation message will be displayed.

The framework has default message settings for each kind of validation failure; for example, when the required validation fails, it displays "This field is required" in the example below:

messages: {
    required: "This field is required.",
    remote: "Please fix this field.",
    email: "Please enter a valid email address.",
    url: "Please enter a valid URL.",
    date: "Please enter a valid date.",
    dateISO: "Please enter a valid date (ISO).",
    number: "Please enter a valid number.",
    digits: "Please enter only digits.",
    creditcard: "Please enter a valid credit card number.",
    equalTo: "Please enter the same value again.",
    accept: "Please enter a value with a valid extension.",
    maxlength: $.validator.format("Please enter no more than {0} characters."),
    minlength: $.validator.format("Please enter at least {0} characters."),
    rangelength: $.validator.format(
                   "Please enter a value between {0} and {1} characters long."),
    range: $.validator.format("Please enter a value between {0} and {1}."),
    max: $.validator.format("Please enter a value less than or equal to {0}."),
    min: $.validator.format("Please enter a value greater than or equal to {0}.")
},

If we want to display our own custom messages then here is how we can override every message in our local web application:

$.validator.messages.required ="My Custom message";

When we click on the Submit button, the code below picks up:

this.submit( function( event ) {
    if ( validator.settings.debug )
        // prevent form submit to be able to see console output
        event.preventDefault();
        
    function handle() {
        if ( validator.settings.submitHandler ) {
            if (validator.submitButton) {
                // insert a hidden input as a replacement
                // for the missing submit button
                var hidden = $("<input type="hidden" />").attr(
                  "name", validator.submitButton.name).val(
                  validator.submitButton.value).appendTo(validator.currentForm);
            }
            validator.settings.submitHandler.call( validator, validator.currentForm );
            if (validator.submitButton) {
                // and clean up afterwards;
                // thanks to no-block-scope, hidden can be referenced
                hidden.remove();
            }
            return false;
        }
        return true;
    }
                    
    // prevent submit for invalid forms or custom submit handlers
    if ( validator.cancelSubmit ) {
        validator.cancelSubmit = false;
        return handle();
    }
    if ( validator.form() ) {
        if ( validator.pendingRequest ) {
            validator.formSubmitted = true;
            return false;
        }
        return handle();
    } else {
        validator.focusInvalid();
        return false;
    }
}

In the above code, validator.form() does the form validation element by element and accumulates the error list, and finally the $.validator.ShowErrors() method will display the errors on the page.

How we do conditional validation

Remember while calling the framework validate() (which returns the validator), we can pass in options as an input parameter. Here I have written a dependency function handler to do a conditional required check. Here I am applying a condition validation on the URL, which says the URL is required only when the value in email is empty (of course, it doesn't make sense to have this kind of a dependency, but this is just to demo the options input parameter).

$("#commentForm").validate(
{
  rules:
  {
      url:
      {
          required: function () {
              return $("#cemail").val().length === 0;
          }
    }
  }
}
);

In the above code, the URL is required only when email is empty; internally, jQuery uses extend() to extend our own custom provided rules with the default settings of the validator. Shown below is how the code settings are extended with our default options sent as input parameters:

$.validator = function( options, form ) {
    this.settings = $.extend( {}, $.validator.defaults, options );
    this.currentForm = form;
    this.init();
};

Points of interest

  1. The jQuery validation framework hides a lot of complex logic and gives the user flexibility to specify rules in a simple manner.
  2. It is highly extensible, we can override existing ones or add new rules/validation methods.
  3. We can have individual teams write their own validation methods and hook them into the existing framework, that is how we can separate the concerns.
  4. Error messages can be customized local to a page.

Disclaimer

This article uses a sample which is not for direct production deployment. This is to give the reader an idea of how jQuery works and how we can use it in our local web applications. It is the individual's responsibility to follow the necessary best practices while implementing Model Binding.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

RamuSangabathula
Architect
India India
I have around 9 years of experience in Microsoft technologies, .Net 2.0,3.5, Asp.net MVC developed small to medium scale products using Silverlight 2.0, Asp.net Ajax technologie and Javascript frameworks.

Comments and Discussions

 
GeneralNice explanation, but i have some question here... Pinmemberkeonglah14-Jun-11 23:47 
GeneralMy vote of 5 PinmemberMonjurul Habib14-Jun-11 11:09 
GeneralMy vote of 5 PinmemberViral Upadhyay14-Jun-11 9:44 
GeneralMy 4! PinmemberRakeshMeena11-Jun-11 19:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.141015.1 | Last Updated 11 Jun 2011
Article Copyright 2011 by RamuSangabathula
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid