Form error messages

This page details a way of showing form validation error messages that is far superior to the silly alerts most forms use.

In my opinion alerts should only be used if the browser doesn't support a better way of displaying form error messages. Instead, the W3C DOM allows us to write error messages next to the form field they apply to. This is clearly the superior method, so we only use alerts if the browser doesn't support advanced scripting.

Example

Try this example form. Every field is required. In addition, I check if the "email" field contains a "@". If it doesn't, the value is not a valid email address and an error message is shown.

The script

var W3CDOM = (document.getElementsByTagName && document.createElement);

window.onload = function () {
	document.forms[0].onsubmit = function () {
		return validate()
	}
}

function validate()
{
	validForm = true;
	errorstring = '';
	var x = document.forms[0].elements;
	for (var i=0;i<x.length;i++)
	{
		if (!x[i].value)
			writeError(x[i],'This field is required');
	}
	if (x['email'].value.indexOf('@') == -1)
		writeError(x['email'],'This is not a valid email address');
	if (!W3CDOM)
		alert(errorstring);
	if (firstError)
		firstError.focus();
	return validForm;
}

function writeError(obj,message)
{
	if (obj.hasError) return;
	if (W3CDOM)
	{
		obj.className += ' error';
		obj.onblur = removeError;
		var sp = document.createElement('span');
		sp.className = 'error';
		sp.appendChild(document.createTextNode(message));
		obj.parentNode.appendChild(sp);
		obj.hasError = sp;
	}
	else
	{
		errorstring += obj.name + ': ' + message + '\n';
		obj.hasError = true;
	}
	if (validForm)
		firstError = obj;
	validForm = false;
}

function removeError()
{
	this.className = this.className.substring(0,this.className.lastIndexOf(' '));
	this.parentNode.removeChild(this.hasError);
	this.hasError = null;
	this.onblur = null;
}

Explanation

First we check W3C DOM support. The example script on this page works (somewhat) in Explorer on Mac, but don't be surprised if this browser craps out in a real web page. Its W3C DOM engine is simply not good enough to support this script in all circumstances. If Explorer Mac crashes, add a line to the W3V DOM detection that refuses it.

Then we create an onsubmit event handler for the form that calls the function validate() (see also the Introduction to forms).

var W3CDOM = (document.getElementsByTagName && document.createElement);

window.onload = function () {
	document.forms[0].onsubmit = function ()	{
		return validate()
	}
}

validate()

We assume that the form is valid (validForm = true) and we create a string errorstring, which will eventually contain all error messages. This string is only for non-W3C DOM browsers.

function validate()
{
	validForm = true;
	errorstring = '';

The core of the validate() function works pretty much as always (see the example form for an example). Check the form fields for any sort of error you wish. When you find an error, call the writeError() function and hand it the faulty form field and an error message.

	var x = document.forms[0].elements;
	for (var i=0;i<x.length;i++)
	{
		if (!x[i].value)
			writeError(x[i],'This field is required');
	}
	if (x['email'].value.indexOf('@') == -1)
		writeError(x['email'],'This is not a valid email address');

If the browser does not support the W3C DOM, generate an alert with errorstring. You might want to rewrite this bit to show an alert for each error message.

	if (!W3CDOM)
		alert(errorstring);

As a service to the user, put the focus on the first faulty field you found.

	if (firstError)
		firstError.focus();

Finally, return validForm. If still true (no errors found) the form is submitted, if false the form submission is halted.

	return validForm;
}

writeError()

The writeError() function tries to write the error message next to the form field. If it fails, because the browser doesn't support the W3C DOM enough, it appends the error message to errorstring.

The function is handed a form field object and an error message.

function writeError(obj,message)
{

First we check if this form field already has an error message. If it does, return to the main validate() function. I don't want to show two or more messages for the same field.

	if (obj.hasError) return;

Check if the browser supports the W3C DOM.

	if (W3CDOM)
	{

If it does, we can start the magic. First of all add error to the className of the form field. Specify the styles of faulty form fields in CSS.

		obj.className += ' error';

Next, set an onblur event handler for the removeError() function. See below.

		obj.onblur = removeError;

Create a <span> tag to hold the error message and give it a class="error". Again, specify the presentation of the error message in CSS.

		var sp = document.createElement('span');
		sp.className = 'error';

Append a text node with the error message to the <span>.

		sp.appendChild(document.createTextNode(message));

Append the entire <span> to the parent node of the form field (in my case, every label/field pair is contained in a <p>).

		obj.parentNode.appendChild(sp);

Finally, set an hasError property for the form field. This property serves both to indicate that this form field has an associated error message, and to point to this error message for easy future removal.

		obj.hasError = sp;
	}

For JavaScriptually challenged browsers we append the name of the form field and the error message to errorstring. This string is alerted at the end of the validation (see above). Also set hasError. It still indicates that the form field already has an associated error message.

	else
	{
		errorstring += obj.name + ': ' + message + '\n';
		obj.hasError = true;
	}

If validForm is still true (if this is the first error message we found), set the firstError variable to point to this form field. validate() uses this information to put the focus on the first faulty form field.

	if (validForm)
		firstError = obj;

Set validForm to false: form is not valid.

	validForm = false;
}

removeError()

Every faulty form field has an onblur event handler that points to this function.

If, after having read the error messages, the user moves away from any faulty form field, I politely assume he has corrected the error. Therefore the error message should disappear.

First remove the error from the form field's class name. This removes the special error styles.

function removeError()
{
	this.className = this.className.substring(0,this.className.lastIndexOf(' '));

Secondly, remove the error message. The hasError property points to the <span> containing the message, so we remove it from the form field's parentNode.

	this.parentNode.removeChild(this.hasError);

Finally a bit of house cleaning. Set the hasError property to null (form field has no more associated error messages) and remove the onblur event handler.

	this.hasError = null;
	this.onblur = null;
}