jQuery Form Validation: theory and practice

jQuery Form Validation: theory and practice

jQuery makes it very easy to validate forms. There are a number of useful plugins, the most prominent being validate . If you are wanting something very lightweight or are just interested in how something like this works, read on. The $.each function allows you to iterate over collections in jQuery, it is really one of the most useful utility methods available. We can iterate over all input elements on a page using $.each like this

var input_count = 0;

$(document).ready(function(){
  $.each($('input'), function(index, value){
    input_count++;
  });
  console.log("Number of inputs:  %i",  input_count);//use Firebug, not alert() !
});

Iterating Over Text Inputs

There is a problem with the above code, any input elements (including buttons) are going to be iterated over, so let’s use a more specific selector to only retrieve input elements of type “text”

...

$.each($('input[type="text"]'), function(index, value){

...

Required Elements

Okay, now we can iterate over our text inputs. So, where to go from here? Do we check that each item has a value? What if some items aren’t required? The easiest way I’ve found to do this is to use a class on required items (I usually call it “required” (creative, huh?)).

...
<form action="" method="POST" id="login_form">
  <dl>
    <dt>
    <dd>Username <span class="req">*</span></dd>
    <dd><input type="text" name="username" class="required" maxlength="7" /></dd>
    <dd>Password <span class="req">*</span></dd>
    <dd><input type="text" name="password" class="required" maxlength="7" /></dd>
    <dd>Favorite Color<dd>
    <dd><input type="text" name="username" /></dd>
    <dd>Finish</dd>
    <dd><input type="submit" name="submit" /></dd>
    </dt>
  </dl>
</form>
...

Now we can check which fields are required and cancel form submission if they are empty. Getting rid of our input counter variable, here is the new code

$(document).ready(function(){
  $('#login_form').submit(function(){//add an onsubmit event handler to our form
    var blank_fields = false;//initialize a generic error detector
    $.each($('input[type="text"]'), function(index, value){
      var t = $(this);//limit the number of new jQuery objects created
      if(t.hasClass('required')){//we know it is required
        if(t.val() == "" || t.val() === undefined){
          console.error('%s can not be blank', t.attr('name'));
          blank_fields = true;
        }
      }
    });
    return !blank_fields;//if there are no blank fields, submit
  });
});

That is a mouthful, but it is actually quite simple. When our form is submitted, we iterate over each input item to see if it is required. If it’s required, check to see if it is empty, if it is empty, return false (cancelling form submission).

Now, there are a couple errors with the code above. For one, it is checking all form inputs (not just the one in our form), so if we had multiple forms on the page, both with required elements, this would fail. Also, being able to check more specific criteria would be nice. We will limit our iteration to input items that are of type text and are descendants of the target form. Let’s also add a maxlength attribute to a form field to ensure that it is within the set character length:

$(document).ready(function(){
  $('#login_form').submit(function(){
    var blank_fields = false;
    var length_error = false;//check for length errors
    $.each($('#login_form input[type="text"]'), function(index, value){//limit input elements to those within the form
      var t = $(this);
      if(t.hasClass('required')){
        if(t.val() == "" || t.val() === undefined){//check if it's empty
          console.error('%s can not be blank', t.attr('name'));
          blank_fields = true;
        }
        if(t.attr('maxlength') !== undefined){//if maxlength attribute is present in the current element
          if(t.val().length > t.attr('maxlength')){//check string length against given maxlength
            console.error('%s is too long: %i chars', t.attr('name'), t.val().length);
            length_error = true;
          }
        }
      }
    });
    return !(blank_fields || length_error);//if there are no blank fields and no length errors, submit
  });
});

Okay, now we are getting somewhere. This could be used as is, but it doesn’t provide very friendly error messages (they have to have Firebug installed to know what the errors are). Let’s wrap up our code so far into a jQuery function and provide some more useful errors. I’ve commented changes to the code, but it is mostly the same.

$.fn.validate = function(options){
  /*
  * Define the default error message tag and styling, allow this to be overridden
  */
  var defaults = {
     formTag: 'div',
     color: '#8A1F11',
     border: '2px solid #FBC2C4',
     errorBG: '#FBE3E4 0 0',
     marginBottom:'1em',
     padding:'0.8em',
     width: $(this).width()
   };
  var o = $.extend(defaults, options);

  /*
  * Private method creates error element
  */
  function genError(msg){
    var err = $(document.createElement(o.formTag));//create an error element
    err.css('border',      o.border)
       .css('background',    o.errorBG)
       .css('margin-bottom', o.marginBottom)
       .css('padding',     o.padding)
       .css('color',     o.color)
       .css('width',     o.width);//Apply css style rules to the error
    err.html(msg);//insert the error message into the element
    return err;
  }

  /*
  * Our slightly modified form validation
  */
  $(this).submit(function(){//now uses $(this) reference as the method is applied to the form selector
    var blank_fields = false;//initialize a generic error detector
    var length_error = false;
    //now we use $(this).find('input[type="text"]') to locate our forms child elements
    $.each($(this).find('input[type="text"]'), function(index, value){
      var t = $(this);
      if(t.hasClass('required')){
        if(t.val() == "" || t.val() === undefined){
          //instead of logging error, create a new element and append it to the input elements parent
          errormsg = genError(t.attr('name') + ' can not be blank')
          t.parent().append(errormsg);
          blank_fields = true;
        }
        if(t.attr('maxlength') !== undefined){
          if(t.val().length > t.attr('maxlength')){
            //same as above but more concise
            t.parent().append(genError(t.attr('name') + ' is too long'));
            length_error = true;
          }
        }
      }
    });
    return !(blank_fields || length_error);
  });
}

Usage:

$(document).ready(function(){
  $('#login_form').validate({color:'#000'});
});

About the Author

Rob McVey

I am a software developer/IT professional helping businesses save money through informed purchase consulting; website development and marketing; and process automation.

2 Comments to “jQuery Form Validation: theory and practice”

  1. Issac Uranga says:

    Noo! Im using my iphone and I can’t seem to be able to open the page right. I will be back to read this tonight when I get back from lecture. The topic seems like something I must read.

  2. Canli Yayin says:

    You are a so much clever person!…