Form Validation Library v. 1.0.4

Copyright (c) 2001, 2002, 2003, 2004, Paul Strack.

Introduction

This document describes an open source JavaScript form validation library: FormValidation.js. The document is broken down into the following sections:

Overview

The purpose of this library is to support complex validations of HTML form values via JavaScript. In practice, there are a number of issues surrounding JavaScript form validation:

This library attempts to address these issues by standardizing and automating as much form validation process as possible. The library's functionality rests on two key objects:

Common Validation Operations: The page developer specifies the properties of the validations object (validation properties) to control how individual fields in the form are validated. The validation properties are used during the page's onload event to initialize form and field validation functions. For example, to mark a field as required, the developer simply sets: validations.field.required = true.

Validation Event Handlers: This library automatically assigns validation functions to the appropriate event handlers of form elements: onchange for text elements, onchange/onblur for select lists, onclick for radio buttons and checkboxes and onsubmit for the form itself. The web developer does not need to assign any form element event handlers.

Getting and Setting Field Values: The formdata object's methods can be used to manipulate field data. The formdata object has getField() and setField() methods for every field. If a form has an "address" field, it can be manipulated via get/setAddress() methods. Furthermore, the getField() method parses the field value to a specified data type and the setField() method formats the value as appropriate when assigning it to the field. Typically, the formdata object is used to write custom validation methods.

This library should function correctly for all 4th generation or later browsers: Netscape Navigator 4.0+ and Internet Explorer 4.0+. It should also work in any other web browser that complies with the ECMA v1 JavaScript standard. This library is open source, using a BSD-style license. See the Form Validation Library License for more details.

Importing the Library

To use this library's functions and objects, you must (a) import the library's .js file and (b) call the library's initFormValidation() method during the page's onload event:

<script src="FormValidation.js"></script>
<body onload="initFormValidation()">

If the library is not located in the same directory as the form itself, you must use the correct path to the library in the script's src attribute. If the page needs additional initialization during the page's onload event, simply call the library's initFormValidation() method from within the page's init() function:

<script>
function init() {
  initFormValidation();
  // Other page initialization ...
}
</script>

<body onload="init()">

Another alternative is to assign the initFormValidation() method to the onfocus event of every field:

<script src="FormValidation.js"></script>
<form ...>
  <input name="id" onfocus="initFormValidation()">
  <input name="age" onfocus="initFormValidation()">
  ...
  <input type="submit" onfocus="initFormValidation()">
</form>

If you use this onfocus event technique, you must make sure that it is applied to every form element, otherwise the form will not be initialized if the user interacts with that element first. This alternative may seem like a poor approach, but it works well if the form elements are generated by some sort of server-side scripting.

Library Requirements

In order for the library to function correctly, the HTML form must obey the following rules:

The rule for unique element names may be broken under specialized circumstances, if the repeated form elements are arranged in groups of related fields. See the section on the formitems array for additional details.

Common Field Validations

This validation library supports simple declarations for the following common single-field validations:

All of these common validations must be assigned to the validations object as validation properties. Before this can be done, a new Validator object for the field must be created and assigned as a validations object. It must be assigned to a property whose name matches the field to be validated:

validations.field = new Validator("Field Label");

Once a Validator object has been created for the field, the validation properties for that field can be assigned to it. A complete list of possible validation properties is given below. Not every field will have every property:

<script>
validations.field = new Validator("Field Label");
validations.field.readonly = true;
validations.field.required = true;
validations.field.min = #;
validations.field.max = #;
validations.field.minlength = #;
validations.field.maxlength = #;
validations.field.datatype = "Type";
validations.field.scale = #;
validations.field.compute = computeFunction;
</script>

Form elements groups which all have the same name (such as radio buttons) only need a single validation object and set of validation properties.

<script>
validations.radioName = new Validator("Field Label");
validations.radioName.compute = computeFunction;
</script>

A field should only have the validation properties appropriate to that field. For example, suppose a loginForm has a userID field that is required and must be between 6 and 10 characters in length. It would have the following validation properties:

User ID: <input type="text" name="userID">
<script>
validations.userID = new Validator("User ID");
validations.
userID .required = true;
validations.
userID .minlength = 6;
validations.
userID .maxlength = 10;
</script>

The field validation properties must be defined in the page after the FormValidation.js library is imported. The best place to define field validation properties is either (a) immediately after each field or (b) in one big block at the end of the HTML page.

The parameter for the Validator constructor is the human-readable label for the field, used for generating error messages. The form's initialization copies validation properties from the validations object to the corresponding form elements. In addition, it creates and assigns a label property to the element whose value matches the parameter of the Validator constructor.

// After form initialization:
document.loginForm.userID.label = "User ID";
document.loginForm.userID.required = true;
document.loginForm.userID.minlength = 6;
document.loginForm.userID.maxlength = 10;

The meaning of each validation properties is as follows:

Field Data Types

Field formatting and parsing may be specified through the field's datatype validation properties The name "datatype" is used rather than "type" to better distinguish it from the HTML/JavaScript type attribute used to designate the type of a form input element ("text", "checkbox", etc.). Each data type has formatting and parsing functions associated with, which are used to manage field values. The appropriate formatting and parsing functions are assigned as methods to the field based on the field's datatype. For example, an itemAmount field in an orderForm might use the "Integer" data type to ensure that the field only holds numeric values:

Amount: <input type="text" name="itemAmount">
<script>
validations.itemAmount = new Validator("Amount");
validations.itemAmount.datatype = "Integer";
</script>

The form validation library has the following predefined data types:

JavaScript developers can define new data types simply by creating appropriate formatType and parseType functions for the new type. Consult the existing formatting and parsing functions for examples. Each field will be assigned the formatting and parsing functions appropriate to the field's datatype. For example, fields with a datatype of "Integer" will be assigned the formatInteger() and parseInteger() functions.

Field formatting is called as part of both field and form validation. Field formatting appropriate to the data type occurs automatically, but not until after the user finishes entering the text and attempts to move to the next field in the form. If the contents of the field cannot be changed to the correct format, an error message alert is generated.

Field parsing is used when field values are needed in calculations within other scripts on the page. Ordinarily, values retrieved from a form field are strings. If a field holds a numeric or date data type, the appropriate parse method is used to retrieve values of a more appropriate JavaScript type (number, Date, etc.). If for some reason parsing fails, the parse methods return a value of NaN (JavaScript's Not-A-Number value). The NaN value is returned for unparseable dates as well as numbers.

Finally, there is an additional validation property only used by decimal fields:

For example, a decimal field with a scale of 3 will always be formatted #.###. Missing values are filled with 0's. Excess values are rounded.

The formdata Object

The library's form initialization assigns getValue() and setValue() methods to each form element. These methods hide the differences between the data manipulation mechanisms of different field types. The methods may be called as follows:

var value = document.form.field.getValue();
document.form.field.setValue(value);

The library also generates a global formdata object that allows the developer to manipulate form field values using simple getField() and setField() methods. For example, if a form has an "address" field, it can be accessed via formdata.getAddress() and formdata.setAddress(value) methods. The methods of the formdata object are:

In each case, Field is replaced by the field's name, with the first letter capitalized. The last two methods are only present if the field has a matching method of the appropriate type (getText and compute). Each method can be called through the global formdata object. For example, the following pairs of method calls are roughly equivalent:

formdata.setField(value);
document.form.field.setValue(value);

value = formdata.getField();
value = document.form.field.getValue();

The above method calls are only identical if the field is not assigned a data type. If the field has a data type with a parse method, then the getField() method will return the parsed value (such as a number or date), while the field.getValue() method the returns the actual field value as an unparsed string. If the field has a data type with a format method, than the setField() method will format the field's value while setting it, while the field.setValue() will not.

As an example, consider the simple form:

User ID:
Password:
Access Level:

It's HTML is:

<table>
<form name="loginForm">
  <tr><td>User ID: </td>
      <td><input type="text" name="userID"></td></tr>
  <tr><td>Password: </td>
      <td><input type="password" name="password"></td></tr>
  <tr><td>Access Level: </td>
      <td><select name="access">
            <option value=""></option>
            <option value="E">Employee</option>
            <option value="A">Administrator</option>
          </select></td></tr>
  <tr><td><input type="submit" value="Submit"></td></tr>
</form>
</table>

It will have the following formdata methods:

The statement formdata.setAccess("E") would select the "Employee" option, and formdata.getUserID() would return the current contents of the userID field. The formdata methods can be used when writing custom validation functions:

<script>
function computeIDforAccess() {
  var error = "";
  var access = formdata.getAccess();
  var userID = formdata.getUserID();
  if ((access != "") && (userID != "")) {
    if (userID.charAt(0) != access) {
      error = "User IDs for " + formdata.getAccessText() +
              "s must begin with " + access + ".\n\n";
    }
  }
  return error;
}
</script>

Custom Validation

No generic library can cover all possible field validations. This library allows JavaScript developers to define custom computations and validations, both for individual fields and for the form as a whole. These custom validation methods are assigned to the compute method of the field's validations object. This compute method will be called along with the other validation methods when the element's event handler is triggered (onchange, onblur,onclick or onsubmit, depending on the field type).

<script>
validations.userID = new Validator("User ID");
validations.userID.required = true;
validations.userID.compute = computeIDforAccess;
...
</script>

A custom compute method should:

The method should return an empty string ("") if everything goes well or a string with an error message if the field is invalid. Any error string should end in "\n\n", so that it display nicely within lists of errors. The compute method can be defined with any name, but for consistency should be in the form computeXxx. If written correctly, the same custom compute method can be used for multiple fields:

<script>
validations.userID = new Validator("User ID");
validations.userID.required = true;
validations.userID.
compute = computeIDforAccess ;

validations.password = new Validator("Password");
validations.password.required = true;

validations.access = new Validator("Access Level");
validations.access.required = true;
validations.access.
compute = computeIDforAccess ;
</script>

When the form is submitted, it calls the compute methods for every field in the form one last time before submission. Under some circumstances, this may lead to extraneous error messages. To avoid this problem, you may also define a global compute method for the form itself. If the form has a compute method, the compute methods for individual fields will not be called when the form is submitted.

<script>
function computeLoginForm() {
  var error = "";
  error += computeIDforAccess();
  // Perform additional global validations ...
  return error;
}

document.loginForm.compute = computeLoginForm;
</script>

To write more generic compute methods, you can use the getValue() and setValue() methods of the field itself. Since the compute method is assigned as a method of the field, you can access these getter and setter methods via this.getValue() and this.setValue(). Similarly, you can use other field validation properties and methods like label and parse(). If properly written, such compute methods can be assigned to any field in any form.

<script>
function computeStartWithLowerCase() {
  var error = "";
  var start = this.getValue().charAt(0);
  if ( (start < "a") || (start > "z") ) {
      error = this.label + " must begin " +
              "with a lowercase letter.\n\n";
    }
  }
  return error;
}

validations.field1.compute = computeStartWithLowerCase;
validations.field2.compute = computeStartWithLowerCase;
</script>

Finally, compute methods can be used to update other fields whose values need to change when the given field's value changes. When writing such custom computations, it is especially helpful that the getField() methods of the formdata object will return the parsed value and the setField() methods will format the field's value while setting it.

In the following example, the computeTaxes() of the total field will automatically update the tax field. Every compute method must return a string value, so it should return an empty string if no errors are possible.

<script>
// Example assumes the existence of "total"
// and "tax" fields in an "orderForm":

validations.total = new Validator("Total");
validations.total.datatype = "Currency";
validations.total.compute = computeTaxes;

validations.tax= new Validator("Tax");
validations.tax.datatype = "Currency";

SALES_TAX = 0.06;

function computeTaxes() {
  var total = formdata.getTotal(); // A number
  if (!isNaN(total)) {
    formdata.setTax(SALES_TAX * total);
    // Formatted as a dollar amount
  }
  return ""; // No errors.
}
</script>

When writing custom validations, be sure to consider the following:

The formitems Array

The form may have elements with the same name (other than radio buttons) under specialized circumstances. In particular, the elements must be arranged in repeated sets of related elements. Under these circumstances, the library will initialize a formitems array. The entries in this array will have get/setField methods for the form field sets.

For example, consider this form:

Item Name Cost per Item Item Amount Cost of All Items
Subtotal:
Tax Rate: %
Total:

It's HTML is:

<table>
<form name="orderForm">
  <tr>
    <th>Item Name</th>
    <th>Cost per Item</th>
    <th>Item Amount</th>
    <th>Cost of All Items</th>
  </tr>
  <tr>
    <td><input type="text" name="itemName"></td>
    <td><input type="text" name="costPerItem"></td>
    <td><input type="text" name="amount"></td>
    <td><input type="text" name="itemsCost"></td>
  </tr>
  <tr>
    <td><input type="text" name="itemName"></td>
    <td><input type="text" name="costPerItem"></td>
    <td><input type="text" name="amount"></td>
    <td><input type="text" name="
itemsCost "></td>
  </tr>
  <tr>
    <td><input type="text" name="itemName"></td>
    <td><input type="text" name="costPerItem"></td>
    <td><input type="text" name="amount"></td>
    <td><input type="text" name="itemsCost"></td>
  </tr>
  <tr>
    <td colspan="3" align="right">Subtotal: </td>
    <td><input type="text" name="subtotal"></td>
  </tr>
  <tr>
    <td colspan="3" align="right">Tax Rate: </td>
    <td><input type="text" name="taxRate"> %</td>
  </tr>
  <tr>
    <td colspan="3" align="right">Total: </td>
    <td><input type="text" name="total"></td>
  </tr>
  <tr>
    <td colspan="3" align="right"></td>
    <td><input type="submit" value="Submit"></td>
  </tr>
</form>
</table>   

The items in the formitems array will have these methods:

These get/set methods with parse and format the specified fields, as appropriate. Like radio buttons, the validation properties for the repeated fields only need to be assigned once. The validations for this form might look like:

<script>
validations.
itemName= new Validator("Item Name");

validations.
costPerItem= new Validator("Cost per Item");
validations.
costPerItem.datatype = "Currency";
validations.
costPerItem.compute = computeTotals;

validations.
amount = new Validator("Item Amount");
validations.
amount .datatype = "Integer";
validations.
amount.compute = computeTotals;

validations.
itemsCost= new Validator("Cost for All Items");
validations.
itemsCost.readonly = true;
validations.
itemsCost.datatype = "Currency";

validations.subtotal = new Validator("Subtotal");
validations.subtotal.readonly = true;
validations.subtotal.datatype = "Currency";

validations.taxRate = new Validator("Tax Rate");
validations.taxRate.datatype = "Decimal";
validations.taxRate.scale = 2;
validations.taxRate.compute = computeTotals;

validations.total = new Validator("Total");
validations.total.readonly = true;
validations.total.datatype = "Currency";

document.orderForm.compute = computeTotals;
<script>

A combination of the formdata object and the formitems arrays can be used to write the computeTotals custom validation method.

<script>
function computeTotals() {
  var subtotal = 0;
  for (var i=0; i<formitems.length; i++) {
    var costPerItem =
formitems[i].getCostPerItem();
    var amount =
formitems[i].getAmount();
    if (isNaN(costPerItem) || isNaN(amount)) {
     
formitems[i].setItemsCost("");
    } else {
      
formitems[i].setItemsCost(costPerItem * amount);
      subtotal += costPerItem * amount;
    }
  }
  formdata.setSubtotal(subtotal);
  var taxRate = 
formdata.getTaxRate();
  if (isNaN(taxRate)) {
    taxRate = 1;
  } else {
    taxRate = 1 + (taxRate / 100);
  }
 
formdata.setTotal(subtotal * taxRate);
  return ""; // No errors
}
</script>

In practice, this form would likely be dynamically generated with a variable number of (possibly pre-populated) item fields. The computeTotals() function accommodates these variations.

Additional Topics

Internationalization: This validation library is prepared for internationalization. To localize the library to a non-US, non-English region, create an extra validation library for the new locale. The name for this localized library should follow the usual internationalization naming conventions, such as FormValidation_sp_ES.js for Spanish localized to Spain or FormValidation_en_GB.js for English localized to Great Britain. The localized library should have localized variations the following items in the core library:

The localized variants should have the exact same names as the original message string variables and formatting/parsing functions. To localize a given HTML page, import the locale-specific library after the core library:

<script src="FormValidation.js"></script>
<script src="FormValidation_sp_ES.js"></script>

The locale-specific error messages and functions will then replace the US-English language messages and functions. This localization should be relatively simple for dynamically generated HTML pages, where the correct localization can be inserted into the page based on the user's preferences. Note that the library's internationalization only covers error messages generated by common validations. Field labels, customized compute validation methods and the page's content will have to be internationalized and localized by the web developer.

Validation Script Generation: In well-designed systems, HTML forms will be dynamically generated by a server-side process using some business object framework. In that case, it should be possible to programmatically determine the correct validations for each field by using the characteristics of the corresponding business objects. The server-side process could then dynamically generate the validation JavaScript along with rest of the HTML form, for the common validations at least.

Automatic script generation can speed development time as well as reducing errors and maintenance cost. The exact code necessary for such script generation depends on both the server-side process used for HTML generation (ASP, JSP, PHP) and the business object framework using to support the business logic.

Form Submission with JavaScript Buttons: In Internet Explorer, when the page focus is in any text element, hitting the [Enter] key will submit the form. To circumvent the problems that arise from the accidental submission of complex forms, many web developers create HTML forms that have no input element of type "submit". Instead, they create a JavaScript button that fulfills the same function:

<input type="button" value="submit"
       onclick="document.form.submit()">

Unfortunately, such a button will circumvent the onsubmit event of the form, skipping the library's form validation. Here is a JavaScript button that will both validate and submit the form:

<input type="button" value="submit"
       onclick="validateAndSubmit()">

<script>
function validateAndSubmit() {
  if (document.form.validate()) {
    document.form.submit();
  }
}
</script>

Library Functions Reference

This documentation includes a few additional references:

In addition, here are several example page's demonstrating the library's use:

Design Notes

This library is a good example of the Facade design pattern, namely a single object (formdata) that provides simplified access to a host of other objects (all the objects representing field elements). It makes extensive use of one of the interesting capabilities of the JavaScript language: the ability to define new methods dynamically and assign them to objects. This allows the library to inspect the characteristics of the form at runtime and dynamically create the formdata facade.

This library uses a custom error handling mechanism: returning string values which are either empty (indicating no error) or are non-empty, containing an error message. This custom error handling mechanism is used rather than the standard error handling of JavaScript 1.5 for backwards compatibility with older browsers.