Download the required resources before beginning this tutorial.
Introduction
So you can see what we're aiming to do, check out this fully functioning example.
Start by opening the file 'form-usability-resources-example.html'. This is a sample section of the form we're going to make more usable and attractive using JavaScript.
Even after reading the excellent Applying CSS
to forms article, this form still isn't as exciting as it could be. Let's brighten up the form by adding some onfocus()
events when an <input>
or <label>
is activated.
The JavaScript
Open the js.js file you downloaded and let's get started!
Registering an event
The first task is to create an event that occurs when the page has loaded. Rather than trying to execute a function call using <body onload="callfunction()">
within the HTML page, we're going to use Simon Willison's addLoadEvent(func)
. This will allow us to add function calls once the page has loaded.
Type the following JavaScript into the .js file:
function addLoadEvent(func)
{
var oldonload = window.onload;
if (typeof window.onload != 'function') { window.onload = func; }
else { window.onload = function() { oldonload(); func(); } }
}
addLoadEvent(presentForm);
Collecting form elements
Create an empty function in your JavaScript file called presentForm
:
function presentForm()
{
}
The second task is to create collections of the form elements that will be manipulated by the JavaScript. The <form>
is nested
in a <div>
with an id="form"
. We can use the getElementById
command to retrieve the <div id="form">
and assign it to the variable eleDiv
. Next, we assign all forms within the <div>
to the variable eleForms
using
the getElementsByTagName
command, as follows:
function presentForm()
{
var eleDiv; var eleForms;
if (document.getElementById && document.getElementsByTagName)
{
eleDiv = document.getElementById("form");
eleForms = eleDiv.getElementsByTagName("form");
}
}
Collecting textareas and inputs
All forms contained within <div id="form">
are now assigned to the variable eleForms
as an HTML
object collection. The next step is to create collections of all the <input>
s and <textarea>
s in this <form>
.
We can do this by looping through all the <form>
s contained within the <div id="form">
and creating more
HTML object collections. We can use the getElementsByTagName
command to assign the <input>
s
to the variable eleInputs
and the <textarea>
s to the variable eleTextAreas
.
function presentForm()
{
var eleDiv; var eleForms;
if (document.getElementById && document.getElementsByTagName)
{
eleDiv = document.getElementById("form");
eleForms = eleDiv.getElementsByTagName("form");
for (var intCounter = 0; intCounter < eleForms.length; intCounter++)
{
eleInputs = eleForms[intCounter].getElementsByTagName("input");
eleTextAreas = eleForms[intCounter].getElementsByTagName("textarea");
}
}
}
The presentForm()
function is almost complete. Our next task is to actually do something with the <textarea>
and <input>
collections.
We'll do this by passing the collections to a new function that will apply an onfocus()
event to each item as follows:
function presentForm()
{
var eleDiv; var eleForms;
if (document.getElementById && document.getElementsByTagName)
{
eleDiv = document.getElementById("form");
eleForms = eleDiv.getElementsByTagName("form");
for (var intCounter = 0; intCounter < eleForms.length; intCounter++)
{
eleInputs = eleForms[intCounter].getElementsByTagName("input");
eleTextAreas = eleForms[intCounter].getElementsByTagName("textarea");
applyFunctionToFormElements(eleInputs);
applyFunctionToFormElements(eleTextAreas);
}
}
}
Applying the onfocus() event
Next we create a new function called applyFunctionToFormElements(htmlObjectCollection)
and loop through the object collection, ready to apply the onfocus()
event:
function applyFunctionToFormElements(htmlObjectCollection)
{
for (var intCounter = 0; intCounter < htmlObjectCollection.length; intCounter++) { }
}
Before applying the onfocus()
event, we'll need to make sure it's not applied to the submit button. The submit button in the example provided
has class="button"
assigned to it which can be used to identify it. Next, we apply the onfocus()
event to the remaining <input>
s
and <textarea>
s.
function applyFunctionToFormElements(htmlObjectCollection)
{
for (var intCounter = 0; intCounter < htmlObjectCollection.length; intCounter++)
{
if(htmlObjectCollection[intCounter].className != "button")
{
htmlObjectCollection[intCounter].onfocus = function () { }
}
}
}
Where it all happens!
Finally we've arrived at the exciting part! Let's use the onfocus()
event to change the appearance of the page. We can set the classes of the form elements themselves,
their siblings, parents or even grandparents. In this example, we'll change the <label>
of the <input>
to bold and the background colour
of the containing <fieldset>
. We'll enter the following code into the onfocus()
function:
this.parentNode.parentNode.parentNode.className = "fieldsetHighlight";<
this.previousSibling.className = "labelHighlight";
The completed function should look like this:
function applyFunctionToFormElements(htmlObjectCollection)
{
for (var intCounter = 0; intCounter < htmlObjectCollection.length; intCounter++)
{
if(htmlObjectCollection[intCounter].className != "button")
{
htmlObjectCollection[intCounter].onfocus = function () {
this.parentNode.parentNode.parentNode.className = "fieldsetHighlight";
this.previousSibling.className = "labelHighlight"; }
}
}
}
We've assigned a pink background to the fieldset <fieldset>
using className="fieldsetHighlight"
and a bold state to the label
<label>
using className="labelHighlight"
. Both of these are predefined classes contained within the CSS:
.fieldsetHighlight
{
border: 1px solid #d7b9c9; background: #f3e6ed; }
.labelHighlight
{
font-weight: bold; }
Clearing elements
If you click one of the <input>
s or <textarea>
s, then the background colour of the fieldset changes to pink and the label to bold.
However, what happens when you click on a second <input>
or <textarea>
in a different <fieldset>
?
The first <input>
or <textarea>
you clicked is still surrounded by the fetching pink but the second item you clicked is also highlighted
and bold. The final task is to loop through the <label>
s and <fieldset>
s and set their colour and font weight back to normal, className=""
.
So we'll insert a new function call at the beginning of the onfocus()
function called clearFieldsetsAndLabels()
, as follows:
function applyFunctionToFormElements(htmlObjectCollection)
{
for (var intCounter = 0; intCounter < htmlObjectCollection.length; intCounter++)
{
if(htmlObjectCollection[intCounter].className != "button")
{
htmlObjectCollection[intCounter].onfocus = function () { clearFieldsetsAndLabels();
this.parentNode.parentNode.parentNode.className = "fieldsetHighlight";
this.previousSibling.className = "labelHighlight";
}
}
}
}
We'll create the clearFieldsetsAndLabels()
function and get all <label>
s and <fieldset>
s contained within
<div id="form">
:
clearFieldsetsAndLabels()
{
var eleDiv; var eleFieldsets; var eleLabels;
eleDiv = document.getElementById("form");
eleFieldsets = eleDiv.getElementsByTagName("fieldset");
eleLabels = eleDiv.getElementsByTagName("label");
}
We then need to pass each of the <fieldset>
and <label>
HTML object collections to a function that resets their class.
clearFieldsetsAndLabels()
{
var eleDiv; var eleFieldsets; var eleLabels;
eleDiv = document.getElementById("form");
eleFieldsets = eleDiv.getElementsByTagName("fieldset");
eleLabels = eleDiv.getElementsByTagName("label");
clearEle(eleFieldsets) clearEle(eleLabels);
}
The final function loops through each of the elements in the HTML object collection and resets their class.
function clearEle(elements)
{
for (var intCounter = 0; intCounter < elements.length; intCounter++)
{ elements[intCounter].className = ""; }
}
Conclusion
And that's it! A simple JavaScript solution that enhances the usability of your feedback forms using unobtrusive JavaScript.
Check out this fully functioning example.