jQuery form validation

We start with a basic form like this:

<form action="#" method="post">

<fieldset>

<legend>Contact information</legend>

<ul>

<li>
 <label for="name">Name:</label>
 <input type="text" name="name" id="name" />
</li>

<li>
 <label for="surname">Surname:</label>
 <input type="text" name="surname" id="surname" />
</li>

<li>
 <label for="email">Email:</label>
 <input type="text" name="email" id="email" />
 
</li>

<li>
 <label for="subject">Subject:</label>
 <input type="text" name="subject" id="subject" />
</li>

<li>
 <label for="message">Message:</label>
 <textarea id="message" name="message" cols="30" rows="15"></textarea>
</li>


</ul>


<p><input type="submit" name="send" id="send" value="Send" /></p>

</fieldset>


</form>

Then we add some basic styles, just to stylize the form a little bit:

body {
 background: #fff;
 color: #333;
 font: 76% Verdana, sans-serif;
}

form {
 margin: 1em 0 0 2em;
 width: 50%;
}

fieldset {
 margin: 0;
 border: 1px solid #ccc;
 padding-bottom: 1em;
}

legend {
 font-weight: bold;
 text-transform: uppercase;
}

fieldset ul {
 margin: 0 auto;
 padding: 0.5em 0;
 width: 90%;
 list-style: none;
}

fieldset ul li {
 overflow: hidden;
 height: 100%;
 padding: 4px 0;
}

label {
 float: left;
 width: 5em;
 padding-right: 0.3em;
 font-weight: bold;
}

input {
 font: 1em Verdana, sans-serif;
}

fieldset ul li input {
 float: left;
 width: 120px;
 border: 1px solid #ccc;
}

textarea {
 width: 300px;
 height: 200px;
 border: 1px solid #ccc;
 font: 1em Verdana, sans-serif;
}

form p {
 margin: 0;
 padding: 0.4em 0 0 7em;
}

form p input {
 background: #666;
 color: #fff;
 font-weight: bold;
}

You can see this basic page here. After setting our base template, we can start to add a little more interaction by providing a simple JavaScript function that will handle the form processing (of course we're doing it with JavaScript only for demonstrative purpose). Before this, we need to add some styles to our stylesheet:

/* JavaScript styles */

ol#data {
 margin: 1em 0;
 padding: 0.3em;
 background: #e7e7e7;
 border: 1px solid #666;
 list-style: none;
}

ol#data li {
 margin: 0.3em 0;
 padding: 3px;
 background: #fff;
}

ol#data li strong {
 display: block;
}
ol#data li p {
 margin-top: 0;
 padding: 0;
}


Then we can write our little script:

function process() {
 var name = $('#name').val();
 var surname = $('#surname').val();
 var email = $('#email').val();
 var subject = $('#subject').val();
 var message = $('#message').val();
 
 var $ol = $('<ol id="data"></ol>').appendTo('form[method=post]');
 $ol.html('<li><strong>Name:</strong><p> ' + name + '</p></li>' +
       '<li><strong>Surname:</strong><p> ' + surname + '</p></li>' + 
       '<li><strong>Email:</strong><p> ' + email + '</p></li>'
        + '<li><strong>Subject:</strong><p> ' + subject + '</p></li>' + 
        '<li><strong>Message:</strong><p>' +
        message + '</p></li>');
}


$(document).ready(function () {
 $('form[method=post]').attr('action', 'javascript:process()');
});

You can see the page in action here. Notice that we're not validating the form yet. First, we need to add a couple of styles to our CSS:

div.error {
 clear: left;
 margin-left: 5.3em;
 color: red;
 background: transparent url("../../img/error.png") no-repeat 100% 0;
 padding-right: 1.3em;
 height: 100%;
 padding-bottom: 0.3em;
 line-height: 1.3;
}

.input-error {
 background: #ff9;
 border: 1px solid red;
}


div.warning {
 clear: left;
 margin-left: 5.3em;
 color: #338;
 background: transparent url("../../img/warning.png") no-repeat 100% 0;
 padding-right: 1.3em;
 height: 100%;
 padding-bottom: 0.3em;
 line-height: 1.3;
}

Then we can write our validation routines with jQuery:

var validateForm = function(){
 var $name = $('#name').val(); 
 var $nameRe = /^[a-zA-Z]{2,20}$/;
 if(!$nameRe.test($name)) {
   
  handleError('#name', 'The name field must contain only letters and cannot be less than 2 characters.');
 
   
 }
 
 var $surname = $('#surname').val();
 var $surnameRe = /^[a-zA-Z]{3,20}$/;
 if(!$surnameRe.test($surname)) {
  
  handleError('#surname', 'The surname field must contain only letters and cannot be less than 3 characters.');
   
   
 } 
  
 
 var $email = $('#email').val();
 var $emailRe = /^[a-z-0-9_+.-]+\@([a-z0-9-]+\.)+[a-z0-9]{2,7}$/i;
 if(!$emailRe.test($email)) {
   
  handleError('#email', 'This is not a valid email address.');
   
 }

 var $subject = $('#subject').val();
 var $subjectRe = /^\s+|<|>|"|\$|&|\/|'|\*|#|@|\\|\.\.|\./;
 if($subject.length == 0 || $subjectRe.test($subject)) {
   
  handleError('#subject', 'The subject field cannot be empty or contain special characters.');
 }
  
 var $message = $('#message').val();
 var $messageRe = /^\s+$/;
 if($message.length == 0 || $messageRe.test($message)) {
   
  handleError('#message', 'The message field cannot be empty.');
 }
  
 if($nameRe.test($name) && 
  $surnameRe.test($surname) && 
  $emailRe.test($email) &&
  $subject.length > 0 &&
  !$subjectRe.test($subject) &&
  $message.length > 0 &&
  !$messageRe.test($message)) {
   return true;
  } else {
   return false;
 }
  
}

function handleError(element, message) {
 element = $(element);
 element.addClass('input-error');
 var $li = element.parent('li');
 var error = $('<div class="error"></div>').text(message);
 error.appendTo($li);
 $(element).keyup(function() {
  $(error).fadeOut(1000, function() {
    $(element).removeClass('input-error');
  });
 });
 
}

function handleWarning(element, message) {
 element = $(element);
 var $li = element.parent('li');
 var warning = $('<div class="warning"></div>').text(message);
 warning.appendTo($li);
}
   


$(document).ready(function() {
 $('form[method=post] :input').each(function() {
  var $input = $(this);
  $input.focus(function() {
  if($('div.error').size()) {return;}
  if($input.attr('id') == "name") {
   handleWarning($input, 'The name field must contain only letters and cannot be less than 2 characters.');
  } else if($input.attr('id') == "surname") {
   handleWarning($input, 'The surname field must contain only letters and cannot be less than 3 characters.');
  } else if($input.attr('id') == "email") {
   handleWarning($input, 'Enter a valid email address, such as name@example.com.');
  } else if($input.attr('id') == "subject") {
   handleWarning($input, 'The subject field cannot be empty or contain special characters.');
  } else if($input.attr('id') == "message") {
   handleWarning($input, 'The message field cannot be empty.');
  }
 });
 $input.blur(function() {
  $('div.warning').fadeOut(1000);
 });
});
 $('form[method=post]').submit(validateForm);
 
});

The above routines perform the following tasks:

  • create error and warning messages
  • create a routine to handle errors
  • create a routine to handle warnings
  • validate user input

You can see the live demo here. Optionally, we could add even more validation routines, but I think that for the sake of simplicity the aforementioned ones will suffice.

Basic form validation with JavaScript

I just found an old script developed some years ago that performs form validation without using jQuery or other JavaScript libraries. I think it might be of some historical interest, so I post it here:

var errors = {
 name: "Please enter your name",
 email: "Not a valid email address. Email address format: name@domain.extension.",
 address: "Please enter your address",
 phone: "Not a valid phone number. Phone number format: nnn.nnn.nnnn.",
 errorbg: "#ffc",
 errorborder: "2px solid #f00",
 showError: function (message) {
  var form = document.forms[0];
  var p = document.createElement("p");
  p.className = "error";
  p.appendChild(document.createTextNode(message));
  form.appendChild(p);
 }
 
};

var warning = {
 email: "Email address format: name@domain.extension.",
 phone: "Phone number format: nnn.nnn.nnnn.",
 showEmail: function () {
  var datalist = document.getElementById("data-list");
  var li1 = datalist.getElementsByTagName("li")[1];
  var p = document.createElement("p");
  p.className = "warning";
  p.appendChild(document.createTextNode(this.email));
  li1.appendChild(p);
 },
 showPhone: function () {
  var datalist = document.getElementById("data-list");
  var li2 = datalist.getElementsByTagName("li")[3];
  var p = document.createElement("p");
  p.className = "warning";
  p.appendChild(document.createTextNode(this.phone));
  li2.appendChild(p);
 }

};


function checkForm() {

 var name = document.getElementById("name").value;
 if (name.length == 0) {
  errors.showError(errors.name);
  var hook1 = document.getElementById("name");
  hook1.style.background = errors.errorbg;
  hook1.style.border = errors.errorborder;
 }
 
 
  

 var email = document.getElementById("email").value;
 var re =/^[a-z0-9_+.-]+\@([a-z0-9]+\.)+[a-z0-9]{2,4}$/i;
 if (!email.match(re)) {
  errors.showError(errors.email);
  var hook2 = document.getElementById("email");
  hook2.style.background = errors.errorbg;
  hook2.style.border = errors.errorborder;
 }
 
 
 var address = document.getElementById("address").value;
 if (address.length == 0) {
  errors.showError(errors.address);
  var hook3 = document.getElementById("address");
  hook3.style.background = errors.errorbg;
  hook3.style.border = errors.errorborder;
 }
 
 
 var phone = document.getElementById("phone").value;
 var re2 = /(\d{3}).(\d{3}).(\d{4})/;
 if (!phone.match(re2)) {
  errors.showError(errors.phone);
  var hook4 = document.getElementById("phone");
  hook4.style.background = errors.errorbg;
  hook4.style.border = errors.errorborder;
 }
 
 if (name.length > 0 && email.match(re) && address.length > 0
     && phone.match(re2) ) {
     return true;
 }
 
 else {
  return false;
 }
 
 
 
 
}

function showResults() {
 var form = document.forms[0];
 var results = document.createElement("div");
 results.id = "results";
 results.innerHTML = "<div class='topright'><div class='bottomleft'><div class='bottomright'>" + "Your data have been saved." +
     "</div></div></div>";
 form.appendChild(results);
}
 
 

window.onload = function() {
 var dataform = document.getElementById("data");
 dataform.action = "javascript:showResults()";
 dataform.onsubmit = checkForm;
 
 var hook5 = document.getElementById("showemail");
 hook5.href = "javascript: warning.showEmail()";
 
 var hook6 = document.getElementById("showphone");
 hook6.href = "javascript: warning.showPhone()";
 
 
 
 
}

The interesting thing to note here is that I used object literals to store error and warning messages. Using object literals is surely a good and easy way to store such a data type within a script.

Common mistakes in form validation

Here are some of the most common mistakes I've seen doing so far while validating forms:

Partial submission

This mistake occurs when a user fills a form without providing all data in the correct format. Generally, valid data is inserted in the database, but the page shows some validation errors and the user must refill the form. What happens then? Simply put, the user get some messages such as "username already in use" because the validation script compares user data with what has already been inserted in the database. So the user must choose another username, another email and so on. Quite frustrating.

Solution

Avoid partial submission. Use sticky forms to keep valid data within form fields, but don't insert anything in the database until the user fills correctly all the fields.

Overuse of regular expressions

Using regular expressions is fine, but this kind of approach should be never overused. Certain data formats are really hard to keep within a regular expression pattern. For example, what is the correct pattern for a person name? In PHP, for instance, the overuse of regular expressions may lead to some performance issues, though in rare cases.

Solution

Use regular expressions only when necessary. If you can, use string manipulation or data types function instead.

Confusing error messages

Error messages should be descriptive, self-explanatory and informative. Otherwise, they only tend to confuse the user.

Solution

Use descriptive and meaningful error messages, such as Phone numbers can only contain digits.

Lack of information

Most websites don't provide information about how a form should be filled. For example, they say that a field with an asterisk is mandatory, but they don't say in which format it should be.

Solution

Provide as much information as you can about how a form should be filled in order to avoid typos and errors.

Inaccessible information

Most websites still rely on colors, visual clues and a massive use of JavaScript to provide information. This kind of approach makes the form almost inaccessible for people with disabilities who use assistive technologies (such as screen readers and textual browsers).

Solution

Make sure that your form is still accessible without colors, visual clues, or JavaScript.