Marcus
Marcus

Reputation: 5828

What is the easiest way to detect if at least one field has been changed on an HTML form?

I have an HTML form with over 20 fields. I also have a couple of links on the page which will lead the user away from the form... potentially without having saved any changes.

I want to warn (JS confirm) the user onClick of these links if any of the form fields have changed, but I don't want to create a huge switch statement that I then need to maintain as I add new fields to the form. I know how to create a long list of 'if' statements in Javascript, naming each of the fields and checking each value, but I don't want to do that if I can get away with it.

What's the easiest way to check if the user has changed at least one of the field values?

Upvotes: 45

Views: 49059

Answers (8)

Jiohban
Jiohban

Reputation: 1

the <form> tag has the onChange callback that is triggered every time any of the fields triggers an onChange event.

Example:

<form onChange={isDirtyFunc}>
   <input name="name" type="text"/>
   <input name="address" type="text"/>
   <input name="age" type="number"/>
</form>

If any of the field changes the isDirtyFunc function is called with the relative event

Upvotes: -1

Dandorid
Dandorid

Reputation: 205

The answer posted earlier has proven itself (after some minor improvements) over the years. This is the function that I use in the onBeforeUnload event function.

/*
** Determines if a form is dirty by comparing the current
** value of each element with its default value.
**
** @param {Form} form the form to be checked.
** @return {Boolean} true if the form is dirty, false otherwise.
*/
function formIsDirty(form) {
    for (var i = 0; i < form.elements.length; i++) {
        var element = form.elements[i];
        var type = element.type;
        switch (element.type) {
        case "checkbox":
        case "radio":
            if (element.checked != element.defaultChecked)
                return true;
            break;
        case "number":
        case "hidden":
        case "password":
        case "date":
        case "text":
        case "textarea":
            if (element.value != element.defaultValue)
                return true;
            break;
        case "select-one":
        case "select-multiple":
            for (var j = 0; j < element.options.length; j++)
                if (element.options[j].selected != element.options[j].defaultSelected)
                    return true;
            break;
        }
    }
    return false;
}

Here is an example the onBeforeUnload handler function:

    function onBeforeUnload(event) {
        event = event || window.event;
        for (i = 0; i < document.forms.length; i++) {
            switch (document.forms[i].id) {
            case "search":
                break;
            default:
                if (formIsDirty(document.forms[i])) {
                    if (event)
                        event.returnValue = "You have unsaved changes.";
                    return "You have unsaved changes.";
                }
                break;
            }
        }
    }

Upvotes: 0

Daniel Žeimo
Daniel Žeimo

Reputation: 163

In my case I combined @Crescent Fresh and @nickf answers and got this one:

var formBefore;

var $inps = $('form').find('input,select,textarea');

$(function() {
    formBefore = $("form").serialize();
});

$inps.change(function () {
    var changedForm = $("form").serialize();
    if (formBefore != changedForm) {
        $("#btnSave").css('background-color', 'green');
    } else {
        $("#btnSave").css('background-color', '');
    }
});

Upvotes: 0

mvdleij
mvdleij

Reputation:

I'm pretty sure this is a bad idea, but I wanted to throw it out there.

Form fields have a way to get the "default value" (i.e. the value the field had when it was loaded), and you can compare that against the current value. A simple loop over all fields removes the need for maintenance if you add fields to the form.

There may or may not be various browser bugs associated with the "default value" properties, so I would not trust this method without extensive testing. The code below is a proof of concept, and is not used (by me) in any real application.

function IsDirty(form) {
    for (var i=0; i<form.elements.length; i++) {
        var field = form.elements[i];
        switch (field.type) {
            case "select-multiple":
            case "select-one":
                var options = field.options;
                for (var j=0; j<options.length; j++) {
                    if(options[j].selected != options[j].defaultSelected) return true;
                }
                break;
            case "text":
            case "file":
            case "password":
                if (field.value != field.defaultValue) return true;
                break;
            case "checkbox":
            case "radio":
                if (field.checked != field.defaultChecked) return true;
                break;
        }
    }
    return false;
}

Upvotes: 14

Crescent Fresh
Crescent Fresh

Reputation: 116980

Approach

  1. serialize the form (and all its values) before showing it (jQuery way, Prototype way)
  2. serialize it again in a "onbeforeunload" event handler

If the two don't match, then they must've changed the form, so return a string (eg "You have unsaved data") from your onbeforeunload handler.

This method allows the form fields to evolve while the "confirm if changed" logic remains the same.

Example (mixed javascript and jquery)

var form_clean;

// serialize clean form
$(function() { 
    form_clean = $("form").serialize();  
});

// compare clean and dirty form before leaving
window.onbeforeunload = function (e) {
    var form_dirty = $("form").serialize();
    if(form_clean != form_dirty) {
        return 'There is unsaved form data.';
    }
};

Upvotes: 72

Real Red.
Real Red.

Reputation: 5039

This could be handled with just one boolean variable, we call it dirty bit handling. If you have observed, generally in web pages, once user performs some edit action on any of the fields the form is considered as dirty(edited)(even if data remains unchanged after editing). When user tries to navigate away from the page user is prompted if he wants to save changes.

As per the standard practice, there is no check if after editing some field if the value actually got changed or not. For eg: If user edits and appends 'xyz' to a text field and then deletes 'xyz' essentially the form data remains the same as it was before but the form is still considered as 'dirty' and user is prompted warning message when he tries to navigate away.

So, if you want to implement this things get pretty simple. You would just need to add onchange() eventhandlers to the controls and set the global boolean variable something like isDirty to true inside those eventhandlers.

Once user wants to navigate away, you can flash a message "There may be unsaved changes on current page. Do you wish to save them?". User won't be disappointed even if he notices that his edit didn't change initial data.

Answers given above implement this very behavior. And I wrote this because you seemed to have an idea to check each and every field by it's initial value to see if it was really altered after edit. Just wanted to tell you that checking every field ain't necessary at all.

Upvotes: 2

jonstjohn
jonstjohn

Reputation: 60266

Here is a one liner that you can add to your forms:

$(':input',document.myForm).bind("change", function() { 
  enablePrompt(true); }); // Prevent accidental navigation away

And then you can make the enableUnloadPrompt() function for your whole site:

function enablePrompt(enabled) {
  window.onbeforeunload = enabled ? "Your changes are not saved!" : null;
}

And finally, before you submit the form properly, make sure to:

enablePrompt(false);

This will not check to see if the form is different in values, only if the form was ever changed by the user. But, it is simple and easy-to-use.

Upvotes: 5

nickf
nickf

Reputation: 545995

Using jQuery this is very easy. You should be able to use the same premise to achieve the same result in vanilla javascript too.

var $inps = $('#myForm').find('input,select,textarea')
  , formAltered = false
;
$inps.change(function() {
    formAltered = true;
    $inps.unbind('change'); // saves this function running every time.
});

The only problem with this is if you change a value, and then change it back to the original, it'll still report the form as altered.

Upvotes: 8

Related Questions