Home > Treasure Chest > AJAX form validation

AJAX design pattern: AJAX form validation

I know there are many tutorials out for AJAX form validation. I needed a validation which I could apply to many form elements, didn't impact much on the HTML, was valid code and flexible enough for mandatory and optional fields. And since I've learned my AJAX from other examples I thought it's a good idea to share my own AJAX solution with you.

This AJAX form validation tutorial works with JavaScript and PHP, but it's easily applicable to ASP as well.

Step 1: Prepare the HTML code for AJAX form validation

For the code to work with many forms we need some conventions which help the AJAX code to know where it's supposed to validate.

Conventions

  • The form name must start with 'ajax_'.
  • The validated field's name and id must match.
  • The validated fields must have a class named 'validate-' followed by 'required-' or 'optional-' and the validation rule(s).
  • The corresponding labels must have an id 'lbl' + (element name without 'txt') to display the error image.
  • All submit buttons (which need to be disabled on wrong input) must have an id that starts with 'ajaxSubmit_'

Let's look at an easy example:

<form name="ajax_form_1" action="..." method="post">
    <label id="lblCompanyName" for="txtCompanyName">Company name:</label>
    <input type="text" name="txtCompanyName" id="txtCompanyName" class="validate-required-textExtended" />
    <input name="theMode" type="submit" value="Save" id="ajaxSubmit_1" />
    <input name="button" type="button" value="Cancel" />
</form>

This form contains one text field with a label and two buttons, one to submit (Save) and one to cancel.

The form's name tells the AJAX code that it should apply validation to this form. The text field has classes which are not related to CSS, rather they tell the AJAX code to perform validation, to insist on a value (required) and to apply the validation rule 'textExtended' (rules are explained later in this article).

The label's id matches the text field's id which is necessary so that the AJAX code can display an error image (an exclamation mark) if the user inputs invalid content. I prefer this solution over initially hidden images, because you don't need additional HTML code.

Finally, the submit button tells the AJAX code to disable it initally and wait for validation results to enable it again.

Step 2: Add AJAX form validation code

Professional Ajax by Nicholas Zakas and others.

I can recommend this book. It's been written by programmers and was a great help for me.

The AJAX form validation code is kept in a separate file. I've written it in such a way that when your form complies to the rules outlined in step 1 you can easily reuse the code for different forms in your application.

To insert the JavaScript code add the following two lines to your header section. The first one loads a library (taken from the excellent book "Professional AJAX" by Nicholas Zakas, Jeremy McPeak and Joe Fawcett) while the second loads my code:

<script type="text/javascript" src="js/zxml.js"></script>
<script type="text/javascript" src="js/ajaxIncrementalFormValidation.js"></script>

Download the library from www.wrox.com quoting the book's ISBN (0-471-77778-1).

Assign event handlers to AJAX forms

The first step the AJAX form validation code takes is assign its handler to those forms whose name starts with 'ajax_':

// Attach function to 'onload' or 'load' event for window.
// This is necessary because the document might already have a load event.
// ----------------------------------------------------------------------------------
if (window.addEventListener) { // W3C-compatible browser
   window.addEventListener('load', setEventHandlers, false);
} else {   // Non-compliant MS browser
   window.attachEvent('onload', setEventHandlers);
}

function setEventHandlers() {
   if (zXmlHttp.isSupported()) {
      // Check which form should be AJAX-enabled. These forms have a name like 'ajax_xxx':
      for ( var i=0; i < document.forms.length; i++ ) {
         if ( document.forms[i].name.substr(0, 5) == 'ajax_' ) {
            oTheForm = document.forms[i];
            // Get all form elements which need validation.
            // These have a class starting with 'validation-' associated with them:
            for ( var j=0; j < oTheForm.elements.length; j++ ) {
               aAllElementClasses = oTheForm.elements[j].className.split(' ');
               // Check if validation class is present:
               for ( var k=0; k < aAllElementClasses.length; k++ ) {
                  if ( aAllElementClasses[k].substring(0, 9) == 'validate-' ) {
                     var formElement = document.getElementById(oTheForm.elements[j].name);
                     formElement.onchange = validateField;
                     if ( aAllElementClasses[k].substring(9, 18) == 'required-' ) {
                        formElement.valid = false; // Default for required fields: invalid
                     } else {
                        formElement.valid = true;  // Default for optional fields: valid (since initially they're empty)
                     }
                     // Reset label's text (in case this function is called somewhen after the initial page load):
                     oLabel = document.getElementById('lbl'+oTheForm.elements[j].name.substr(3));
                     oLabel.innerHTML = resetLabelText(oLabel);
                  }
               } // for (element classes)
            } // for (form elements)
            toggleSubmitButtons(oTheForm, true); // Default: Disable all submit buttons
         } // if ajax
      } // for (forms)
    }
};

First we check if the xmlHttp request method is supported through a call to the library. If it is we loop through the collection of forms found in your document. If their name starts with the 'ajax_' string we found a form to attach the handlers to which deal with the form validation.

Once we have the form's object we can inspect its elements, to be precise, its elements' class names. Since multiple class names have to be separated by a space, we split the names into an array using this character.

Now we can traverse the resulting array and look for classes who start with 'validate-'. If we find a match the next step is to get an element reference through its id. Then we can assign the onchange-validation handler to this element.

Initially we assume that every field which requires validation is invalid, forcing the validation to prove the opposite. But what about optional fields? They are valid initially because if they were invalid we would never have a form which gets valid.

Now we get a reference to the associated label and reset its text. This removes any error images which might have been applied at an earlier validation. Finally we disable all submit buttons (which match our convention) to force the user to input his data so we can validate it.

The AJAX form validation handler

Lets check out the event handler which we assigned to the text field of our example with the previous JavaScript code.

function validateField(oEvent) {
   oEvent = oEvent || window.event;
   var formElement = oEvent.target || oEvent.srcElement;

   // Get name of the form to which this element belongs:
   var oForm = getParentForm(formElement);

   var oXmlHttp = zXmlHttp.createRequest();
   
   // Get form element's classes and extract the one which represents the validation rule.
   // (scheme: "validation-hereIsTheRule"). Need to extract from possible other classes:
   var sClassName = formElement.className;
   var iValidationClassStart = sClassName.indexOf('validate-');
   var iValidationClassEnd   = ( sClassName.indexOf(' ') > 0 ) ? sClassName.indexOf(' ') : sClassName.length;
   var sValidation = sClassName.substr(iValidationClassStart, iValidationClassEnd);
   
   // Add form element's name and validation rule to url:
   var sURL = "/ajaxValidateForm.php?formElement=" + formElement.name + "&value=" + encodeURIComponent(formElement.value);
   sURL += '&validation=' + sValidation;
   var boolIsFormValid = false;

   oXmlHttp.open("get", sURL, true);
   oXmlHttp.onreadystatechange = function() {
      if (oXmlHttp.readyState == 4) {
         if (oXmlHttp.status == 200) {
            var arrInfo = oXmlHttp.responseText.split("||");
            var sImage = '<img src="/images/exclamation.jpg" width="20" height"20" title="' + arrInfo[1] + '" />';
            var oLabel = document.getElementById('lbl'+formElement.name.substr(3));
            sLabelText = resetLabelText(oLabel);
            
            if (!eval(arrInfo[0])) {
               oLabel.innerHTML  = sLabelText + sImage;
               formElement.valid = false;                    
            } else {
               oLabel.innerHTML  = sLabelText;
               formElement.valid = true;
            }
            
            // Enable or disable all Ajax submit buttons on this form:
            boolIsFormValid = isFormValid(oForm);
            toggleSubmitButtons(oForm, !boolIsFormValid);
         } else {
            alert("An error occurred while trying to contact the server:"+oXmlHttp.status+', '+oXmlHttp.responseText);
         }
      }
   };
   oXmlHttp.send(null);
};

The function is called whenever an element's value changes (onchange). At this point we don't know anything from any previous JavaScript code. Hence the first task is to get to know in which form and element the content has changed.

Then we can create the xmlHttp request using a function from the library. Next we have to extract the validation part of the class names so we can pass them on to the actual validation code. We have to remember here that an element's class property might contain other class names after the validation class name.

Now we prepare the URL we will pass to the xmlHttp call. It contains the form element's name, its content and the validation string. We assume that initially the form is invalid, storing this in a boolean variable.

Next we open the xmlHttp request, assign the onReadyStateChange handler and send the request.

When the AJAX execution comes back, i.e. when the onReadyStateChange handler has fired, we filter for the valid, completed state. At this moment the server has responded and put its message into responseText.

The AJAX code on the server responds with a message of the format 'valid||message' with 'valid' being either true or false and 'message' containing any error message for the user. We split this string and put each part in an array.

Then we prepare a string containing HTML code pointing to the image (exclamation mark, amend path if necessary) which pops up in the label and shows the error message in its title. Another variable remembers the label's text.

When the validation function returned false the label is assigned its text plus the image. Now the user sees the exclamation mark, and when they hover over it it displays the error message that came with the request's response. If the validation result was true, the label gets the text only thus removing the image from the user's view. Simultaneously the element is assigned a 'valid' property reflecting the validation result.

In the next step we call another function which tells us if the entire form is valid. Depending on this result we either enable the submit buttons or keep them disabled.

Helper subroutines

With the two main functions explained all that needs to be done is have a look at the little helper routines.

function resetLabelText(oLabel) {
// Resets the label's text to what it was before the error image was added.
   if ( !oLabel) { alert('Missing label for field which should be validated.'); }
   sLabelText = oLabel.innerHTML.toUpperCase(); // Better be safe. IE adds dynamically generated tags in upper case!
   iImagePos  = sLabelText.indexOf('<IMG');
   if ( iImagePos != -1 ) {
      return oLabel.innerHTML.substr(0, iImagePos);
   } else {
      return oLabel.innerHTML;
   }
}

This routine is called by the function which assigns the onchange handlers. It removes any image element from a label's text. Since we add an image in case of an error we have to remove this image when the validation returns true.

There are just two things to note: Firstly the alert message which helps when you add new forms and forget to assign a label to a text field you want to validate. Second that we transform the label's inner HTML to upper case because IE returns it in upper and Firefox in lower case.

function getParentForm(oElement) {
   while ( oElement.nodeName != 'FORM' ) {
      oElement = oElement.parentNode;
   }
   return oElement;
}

This little function returns the first parent of an element which is of type 'form'. To do this it traverses the DOM using parentNode and nodeName.

function toggleSubmitButtons(oForm, boolDisabled) {
   for ( var j=0; j < oForm.elements.length; j++ ) {
      if ( oForm.elements[j].id.substring(0, 11) == 'ajaxSubmit_' ) {
         oForm.elements[j].disabled = boolDisabled;
      }
   }
}

Now you know why one convention was to start each submit button's name with 'ajaxSubmit_'. This function finds each element of a form with this prefix and sets it disabled property according to the second parameter it gets. This way we can toggle their state.

function isFormValid(oForm) {
   var boolValid = true;
   for (var i=0; i < oForm.elements.length; i++) {        
      if (typeof oForm.elements[i].valid == "boolean") {
         boolValid = boolValid && oForm.elements[i].valid;
      }
   }
   return boolValid;
}

Remember our validateField function above? It checked each element in the form to be validated by a call to this function. The function checks each element's 'valid' property for a variable type 'boolean'. Should an element not have this property the result would be 'undefined'.

Boolean values are then combined, hence if any one of them is 'false' the overall result will be 'false' as well.

Step 3: AJAX server code

So far we only had a look at the client side of our AJAX form validation code. But what happens when the xmlHttp request is sent? The server code is called with a URL like this:

ajaxValidateForm.php?formElement=txtCompanyName&value=my%20input%20value&validation=validate-required-textExtended

In this example we program the server code in PHP, but you can easily rewrite it into ASP. The main code is as follows:

<?php
   $valid = "false";
   $message = "An unknown error occurred.";
	
   $formElement  = $_GET['formElement'];
   $elementValue = $_GET['value'];
   /* Cut validation rule from form element's class
      Scheme: class="validation-required-hereIsTheRule" 
               or   "validation-optional-anotherRule"
   --------------------------------------------------- */
   $validationArray   = explode('-', $_GET['validation']);
   if ( isset($validationArray[1]) ) {
      $validationRequired = ( $validationArray[1] == 'optional' ) ? false : true;
      $validationRule     = $validationArray[2];
   } else {
      $response = 'false||Missing validation information for element \'' . $formElement . '\'.';
      echo $response;
      exit();
   }

   switch ($validationRule) {
   case 'textExtended':
      // Text as above, but allows also ' " ( ) + : ! / & # $ % ; \ _
      // -------------------------------------------------------------------------------------
      $response = checkInput ('/^[\w\s\.,\-\'\"()\+:;_!\/&#\$%\\\\]+$/', 'Some of these characters are not allowed here.');
      break;
   default:
      $response = 'false||Missing pattern in ajaxValidateForm.php.';
   } // switch $validationRule

   //$response = checkForSpecialTreatment($formElement, $response);
   
echo $response;
?>

Again we assume that validation fails initially presetting the validation result and message accordingly. Then we extract the URL parameters into variables. Note that the validation parameter is assigned into an array, hence array element 0 contains the keyword 'validation', array element 1 if validation is 'required' or 'optional' and array element 2 the validation rule (e.g. 'textExtended' as in this example).

Note the error message which informs you if you forgot to add a validation rule for a particular element.

The heart of this code follows in the switch-statement. I've only written one case for our text field example, but it's here where you would add all your input type validation cases, e.g. 'text', 'email', 'integer', 'float' and so on. They are implemented using regular expressions which are readily available on the Internet. A good tool to test them is the free "Regex Coach" application (Google it).

Both the regular expression and the corresponding error message are handed down to the checkInput routine:

function checkInput ($thePattern, $errorMessage) {
// Validates the element's value against the pattern and sets the error message according to validation result.
// ------------------------------------------------------------------------------------------------------
   global $elementValue;
   global $validationRequired;
   
   $valid   = 'false';                                // Default: validation failed
   $message = 'An unknown error occurred.';

   if ( !preg_match( $thePattern,  $elementValue) ) { // If validation failed...
      if ( strlen($elementValue) > 0 ) {              // ...and a value was found in the element
         $message = $errorMessage;                    //    output the error message
      } else {                                        // If element was empty...
         if ( $validationRequired ) {                 // ...and validation is required
            $message = 'A value is required here.';   //    ask for this value
         } else {
            $valid   = 'true';                        //    otherwise allow empty optional field
            $message = '';
         }
      }
   } else {                                           // If validation succeeded...
      $valid   = 'true';                              // ...everthing's fine. Overwrite defaults
      $message = '';
   }
   
   return "$valid||$message";                         // Return string for JavaScript code
}

This function manipulates the error message. If the element's value is not null (its length larger than zero) it maintains the original error message. If the value is null (e.g. the text field is empty) and validation is mandatory it modifies the error message. If validation is not required and the value is null validation becomes true. The same applies if the regular expression yields no error.

AJAX form validation in action

What happens when the HTML page is loaded? After the external JavaScript has been loaded the setEventHandlers function is called and the onchanage handler is attached to the text field. Then the page waits for your input.

As soon as you change the text field's value and set the focus to anything else the onchange event fires, triggering the execution of the attached validateField function.

validateField then prepares the URL and calls the code on the server. This code returns either true or false and an appropriate error message. If an error was detected validateField adds an image to the text field's label which is displayed notifying the user of the input error. The submit button remains disabled.

Only if the test yielded a 'true' result is the submit button enabled and the user can complete their input procedure.

Special cases

While the code we've treated so far works there are two special cases I'd like to discuss.

Editing values retrieved from a database

There is a problem which is not very obvious when you want to edit values your code retrieves from a database. Assume you've implemented our example and your server code populates our text field with a value. What happes to our form and the submit button?

We assume that data validation fails initially. See function setEventHandlers in the innermost if-else statement where we assign 'false' to the element's 'valid' property. Initialised to 'false' and pre-populated with the retrieved value our text field would never validate to 'true' unless we change it, trigger validation which then in turn changes the 'valid' property to 'true'.

How can we change this?

The answer is a new function which we call each time we retrieved values from a database and populated our form.

function prepareItemEdit() {
// Prepare editing an item: populates & shows form
   populateLowerForm(...);
   makeFormValid(document.ajax_form_1); // Make all form elements 'valid' because data comes from database (function is in AJAX code)
}

This code snippet comes from our HTML page and is called when the Edit button is clicked. It populates the form (I don't discuss this function further) and then makes the form valid, i.e. it will assign 'true' to each of the element's 'valid' property. This function is as follows:

function makeFormValid(oForm) {
// Sets all form elements which have the custom property 'valid' defined to 'true'.
// Used to make entire form valid initially e.g. when loaded with database values.
   for (var i=0; i < oForm.elements.length; i++) {
      if (typeof oForm.elements[i].valid == "boolean") {
         oForm.elements[i].valid = 'true';
      }
   }
}

Add this code to your AJAX code file. It sets the 'valid' property of each element of the form to 'true'. Hence if you don't change any value the form validates. If you change a value the onchange handler ensures proper validation of your changes.

Validating for special conditions

Sometimes the check against a general rule is not enough. Imagine our text field is labeled "Username" and you like to verify if the username is already taken.

The good news is that you can still use the functions we discussed above. What we need to do is check for the text field's name and then apply the special test for that field. This routine is called in the AJAX server code I introduced earlier and is named checkForSpecialTreatment(). You have to remove the comment to enable it in your code.

function checkForSpecialTreatment($formElement, $message) {
// Checks element's name. If a match is found, it tests for any special treatment of this value, 
// e.g. match it against any database values.
// NOTE: When adding elements here, make sure they've got *very special* names to keep them unique.
// ------------------------------------------------------------------------------------------------
   global $elementValue;
   // If previously an error has already occured, don't bother checking any further:
   if ( substr($message, 0, 4) != 'true' ) { return $message; }
   
   require_once("_database.inc.php");
   $valid = '';

   switch ( $formElement ) {
   case 'txtUsernameAdminClients':
      $userName = strtoupper($elementValue);
      if ( strlen($userName) > 0 ) {
         $query = "SELECT username FROM table_users
                   WHERE username = '".$userName."'";
         $result = mysql_query($query) or die(include "error.php");
         if ( mysql_num_rows($result) > 0 ) {
            $valid   = 'false';
            $message = 'This username already exists in the database.';
         }
      }
      break;
   } // switch
   
   if ( $valid != '' ) {
      return "$valid||$message";  // Overwrite $message only if error occured
   } else {
      return $message;            // Otherwise return $message unchanged
   }
}

The function is called each time the AJAX server code tests input values. The parameter $message gets the validation result of the previous code. If the first part is 'false' there is no point in testing for other conditions and the parameter is returned unchanged.

If validation was 'true' so far we can proceed to test for special conditions. In this case we load the database connection file and then select a case according to the text field's name. I encourage you to give it a very special name so you can add tests for other special validations later.

Then we can ask the query and evaluate the result. If this additional validation failed we overwrite the $message parameter, otherwise we leave it unchanged again.