How to Check That an HTML Form Has Been Changed

    Craig Buckler

    Forms. Your HTML web application would fail without them — they’re the basis of most user data transmissions between the browser and server. You’ve no doubt read many articles describing the form tags and data validation with HTML5 or JavaScript. However, today we’ll discuss how to check whether the user has changed form data.

    Why Check for Form Updates?

    There are many reasons why you might want to check whether a form has changed. For example, if a user has updated one or more fields but clicks away from the page, you could display an “updates were not saved” warning. You could even give them the option to save the data (via Ajax). Alternatively, if no updates are made, your application’s efficiency could be improved by not attempting to validate or re-save the data on the server.

    The JavaScript OnChange Event — and Why It Can’t Be Used

    You can attach a JavaScript onchange event handler to any HTML form element. While that appears to be a viable method — and I’ve seen used elsewhere — there are a number of problems with the approach:

    • If the user changes a value then changes it back, the application will still think an update has occurred.
    • If a form value is updated using JavaScript, the onchange event handler won’t be fired.
    • Adding onchange handlers to every element on a large forms incurs a browser processing overhead.
    • If elements are added to or removed from the form you will need to attach and detach event handlers accordingly.
    • The onchange event for checkboxes and radio buttons do not work as expected in a certain browser. (I suspect you can guess which one!)
    • There’s a far easier method…

    Comparing Default Values

    Fortunately, we don’t need to go through the rigmarole of complex event handling. Every form element has a default value associated with its object, i.e., the data the form control showed when the page loaded. This can be checked against the current value to discover whether a change has been made.

    Unfortunately, the default value properties differ between form element types…

    Textual <input> and <textarea> Fields

    Let’s start with the easy elements. All textarea and input tags which are not “checkbox” or “radio” types have a defaultValue property. We can compare this string against the current value to determine whether a change has occurred, e.g.:

    <!-- name input -->
    <input type="text" id="name" name="name" value="Jonny Dough" />
    var name = document.getElementById("name");
    if (name.value != name.defaultValue) alert("#name has changed");
    note: New HTML5 input types

    If you’re using HTML4 or XHTML, your text input types will either be “text,” “hidden,” “password” or “file.” The new types introduced in HTML5 also have a defaultValue property and can be inspected in the same way. This includes email, tel, url, range, date, color, and search.

    Checkboxes and Radio Buttons

    Checkboxes and radio buttons have a defaultChecked property. This will either be true or false and it can be compared against the element’s checked property, e.g.:

    <!-- newsletter opt-in -->
    <input type="checkbox" id="optin" name="optin" checked="checked" />
    var optin = document.getElementById("optin");
    if (optin.checked != optin.defaultChecked) alert("#optin has changed");

    Note that checkboxes and radio buttons also have a defaultValue property, but it’s whatever was assigned to the value attribute — not the current state of the button.

    Drop-down <select> Boxes

    If you’re using a select box, it’ll most probably be one which allows the user to choose a single item from a drop-down list.

    Here’s where it gets a little complicated. The select box itself does not provide a default value property, but we can inspect its (array-like) collection of option elements. When the page is loaded, the option with a ‘selected’ attribute has its defaultSelected property set to true.

    We can retrieve the currently selected option’s index number from the select node’s selectedIndex property. Therefore, if that option has it’s defaultSelected set to false, the select box must have changed, e.g.:

    <!-- job title select box -->
    <select id="job" name="job">
    	<option>web designer</option>
    	<option selected="selected">web developer</option>
    	<option>graphic artist</option>
    	<option>IT professional</option>
    var job = document.getElementById("job");
    if (!job.options[job.selectedIndex].defaultSelected) alert("#job has changed");

    This code will work for any single-choice select box where one option has a ‘selected’ attribute. Unfortunately, there are a number of catches:

    • If no options have a ‘selected’ attribute, the browser will default to the first — but it’s defaultSelected property will be false.
    • If two or more options have a ‘selected’ attribute (illogical, but possible), all will have the defaultSelected property set to true, but the browser can only default to the last one.
    • A multiple select box allows the user to highlight any number of options:
    <!-- skills multi-select box -->
    <select id="skills" name="skills" multiple="multiple">
    	<option selected="selected">HTML</option>
    	<option selected="selected">CSS</option>
    	<option selected="selected">JavaScript</option>

    Multiple-choice select boxes are not popular — probably because a series of checkboxes offers a more user-friendly interface. However, when they are used, more than one option can have its defaultSelected property set to true. The select node’s selectedIndex property is not valid so we must loop through each option in turn to discover whether its selected property matches the defaultSelected property.

    The following code will check for changes to any select box no matter how the options are defined:

    	skills = document.getElementById("skills"),
    	c = false, def = 0, o, ol, opt;
    for (o = 0, ol = n.options.length; o < ol; o++) {
    	opt = skills.options[o];
    	c = c || (opt.selected != opt.defaultSelected);
    	if (opt.defaultSelected) def = o;
    if (c && !skills.multiple) c = (def != skills.selectedIndex);
    if (c) alert("#skills has changed");

    That’s how you check whether any form element has changed.

    But wouldn’t it be great if we had a generic, reusable JavaScript function which could detect changes to any form, worked in all browsers and didn’t require a chunky library? Keep an eye on SitePoint — it’ll be coming very soon!