Invalid Field Indicators Part 2
I hope you liked, found useful, or weren't too bored with the first section of this article. In this section we'll get a bit fancier and illustrate some more ways to let your users know when they have an error on a form.
Field Highlighting
The biggest problem with the alert and error message options from the first article is that once you've let the user know that there is a problem you have done nothing to help them find the problem. For smaller forms (like the demo form we're using here) that's not really an issue, but for larger forms, or more complex forms it can be frustrating to find out what needs to be fixed. To help the user figure it out we can highlight any fields that we find that have an error in them. It is still a good idea to let the user know that there is a problem with an alert or other message - that way they know why the fields have been marked.
To make the following work, I did make a small change to the demo form, the
table cell that contained the preferences radio buttons, now has an id:
<td id="preference">
You'll see why in a moment. Here is the new code for the validateForm function
function validateForm(theForm){
var errMsg = "";
var temp = "";
isValid = true;
temp = validateElement(theForm.fullName, "text", "full name");
if (temp != ""){
errMsg += temp + "\n";
setRequiredWarning(document.getElementById("fullName"), true);
} else {
setRequiredWarning(document.getElementById("fullName"), false);
}
temp = validateElement(theForm.phone, "phone", "phone");
if (temp != ""){
errMsg += temp + "\n";
setRequiredWarning(document.getElementById("phone"), true);
} else {
setRequiredWarning(document.getElementById("phone"), false);
}
temp = validateElement(theForm.email, "email", "email");
if (temp != ""){
errMsg += temp + "\n";
setRequiredWarning(document.getElementById("email"), true);
} else {
setRequiredWarning(document.getElementById("email"), false);
}
temp = validateElement(theForm.location, "select", "location");
if (temp != ""){
errMsg += temp + "\n";
setRequiredWarning(document.getElementById("location"), true);
} else {
setRequiredWarning(document.getElementById("location"), false);
}
temp = validateElement(theForm.preference, "radio", "preference");
if (temp != ""){
errMsg += temp + "\n";
setRequiredWarning(document.getElementById("preference"), true);
} else {
setRequiredWarning(document.getElementById("preference"), false);
}
if (errMsg != ""){
alert(errMsg);
isValid = false;
}
return isValid;
}
The only difference is that we have added calls to a new function named setRequiredWarning, this function is fairly simple, it just turns on or off the error indicator for the passed in field. We are still using the external validator that does the actual validation, and then based on the return from that we either set the field as having a problem or we set it as not having a problem. It is a good idea to specifically set it as not having an error in case it takes them several tries to correct them all, this way as they fix them they go away. The setRequiredWarning function is shown here:
function setRequiredWarning(theElem, setWarning) {
if (setWarning) {
theElem.style.border = "1px solid #010066";
theElem.style.backgroundColor = "#ff0404";
} else {
theElem.style.border = "2px inset #000000";
theElem.style.backgroundColor = "#FFFFFF";
}
}
One of the catches here is that not all form elements readily show changes to their style, checkboxes and radio buttons are the best examples. The easiest work around, if your form is in a table, is to just highlight the table cell that the element is in, (like we do here) otherwise you can surround it in a span or div. Another gotcha is that sometimes when you reset an element it will look a bit different from the rest of the elements, this is especially true if you rely on the default look and feel. The best way to overcome this is to specifically set the style of all of the elements so that when you have to clear an error condition it will look exactly like it is supposed to.
Field Pointers
The next step up from simply highlighting the fields would be to highlight the field and include the nature of the problem in the same place - right next to the problem itself. You're still going to want to use some sort of alert, just to let the user know that there is an issue in case they don't notice the highlighted fields - they could be off screen. A few things changed here, first the table structure - we added another <td> in each row to hold the error message. Yes, everyone says tables are bad and you could accomplish the same thing in CSS, but lets face it, tables are easy, and if there is anything that tables are especially good for, it is forms. Here is the form itself.
<form name="testForm" onsubmit="return validateForm(this);">
<table>
<tr>
<td>
Full name:
</td>
<td nowrap>
<input type="text" name="fullName" id="fullName" />
</td>
<td>
<div id="fullNameError" style="display:none; color: red;"></div>
</td>
</tr><tr>
<td>
Phone number:
</td>
<td>
<input type="text" name="phone" id="phone" />
</td>
<td>
<div id="phoneError" style="display:none; color: red;"></div>
</td>
</tr><tr>
<td>
Email:
</td>
<td>
<input type="text" name="email" id="email" />
</td>
<td>
<div id="emailError" style="display:none; color: red;"></div>
</td>
</tr><tr>
<td>
Location:
</td>
<td>
<select name="location" id="location">
<option>Select a location below</option>
<option value="N">North</option>
<option value="S">South</option>
<option value="E">East</option>
<option value="W">West</option>
<option value="U">Up</option>
<option value="D">Down</option>
</select>
</td>
<td>
<div id="locationError" style="display:none; color: red;"></div>
</td>
</tr><tr>
<td>
Preference:
</td>
<td id="preference">
<input type="radio" name="preference" id="prefApples" value="apples" /> Apples
<br/>
<input type="radio" name="preference" id="prefOranges" value="oranges" /> Oranges
</td>
<td>
<div id="preferenceError" style="display:none; color: red;"></div>
</td>
</tr><tr>
<td colspan="2" align="center">
<input type="button" value="Cancel" onclick="window.location='formValidator.aspx';" />
<input type="submit" value="Submit" />
</td>
</tr>
</form>
The same effect could be accomplished by placing the divs inline with the elements but then you can run into problems with positioning. With the added td element you know exactly where they are going to go.
The validateForm function also changed, instead of passing the actual element to setRequiredWarning, we are passing the name of the element. We had to do this to allow for checkboxes and radio buttons and other elements where the error indication is not on the actual form element. Also added is the actual error message to be displayed for each element. It is possible to hard code this message right in the error div itself, but doing it this way allows for different error messages for different validation problems. You could have one message if the field is left blank and another if the value is not formatted correctly. The message is whatever is returned from the validateElement function, since it knows exactly what is wrong with the element. Here is the new function…
function validateForm(theForm){
var temp = "";
isValid = true;
temp = validateElement(theForm.fullName, "text", "full name");
if (temp != ""){
isValid = false;
setRequiredWarning("fullName", true, temp);
} else {
setRequiredWarning("fullName", false, temp);
}
temp = validateElement(theForm.phone, "phone", "phone");
if (temp != ""){
isValid = false;
setRequiredWarning("phone", true, temp);
} else {
setRequiredWarning("phone", false, temp);
}
temp = validateElement(theForm.email, "email", "email");
if (temp != ""){
isValid = false;
setRequiredWarning("email", true, temp);
} else {
setRequiredWarning("email", false, temp);
}
temp = validateElement(theForm.location, "select", "location");
if (temp != ""){
isValid = false;
setRequiredWarning("location", true, temp);
} else {
setRequiredWarning("location", false, temp);
}
temp = validateElement(theForm.preference, "radio", "preference");
if (temp != ""){
isValid = false;
setRequiredWarning("preference", true, temp);
} else {
setRequiredWarning("preference", false, temp);
}
if (!isValid){
alert("Please correct the indicated errors.");
}
return isValid;
}
Finally, we updated the setRequiredWarning function to display the new error message.
function setRequiredWarning(theElemName, setWarning, errorMsg) {
var theElem = document.getElementById(theElemName);
var errorDiv = theElemName + "Error";
if (setWarning) {
theElem.style.border = "1px solid #010066";
theElem.style.backgroundColor = "#ff0404";
document.getElementById(errorDiv).style.display = "";
writeInnerHtml(errorMsg, errorDiv);
} else {
theElem.style.border = "2px inset #000000";
theElem.style.backgroundColor = "#FFFFFF";
errorDiv.style.display = "none";
}
}
This sort of error indicator can be changed as needed based on your requirements or what your client desires. Change the highlight color of the fields that have errors, use images to indicate the error fields, you could even have the fields blink on and off - yea, right - better not. I've tried to separate each function by what it does, validateForm knows what fields to validate and sends them to validateElement which knows how to validate each element, finally setRequiredWarning knows how to display error messages and mark fields as invalid. Sure it could combined for simpler forms or even factored out further for more complex ones.
On the last page of this article I'll take a look at validating fields as the user enters them, sort of cool, but mostly irritating….
If you have a question, comment, bug fix, or addition let us know. We'll add it
to the demo with the proper credit. Just drop us an
email at comments@directedinsight.com
