Generic way to detect if html form is edited

Viewed 110574

I have a tabbed html form. Upon navigating from one tab to the other, the current tab's data is persisted (on the DB) even if there is no change to the data.

I would like to make the persistence call only if the form is edited. The form can contain any kind of control. Dirtying the form need not be by typing some text but choosing a date in a calendar control would also qualify.

One way to achieve this would be to display the form in read-only mode by default and have an 'Edit' button and if the user clicks the edit button then the call to DB is made (once again, irrespective of whether data is modified. This is a better improvement to what is currently existing).

I would like to know how to write a generic javascript function that would check if any of the controls value has been modified ?

8 Answers

In pure javascript, this would not be an easy task, but jQuery makes it very easy to do:

$("#myform :input").change(function() {
   $("#myform").data("changed",true);
});

Then before saving, you can check if it was changed:

if ($("#myform").data("changed")) {
   // submit the form
}

In the example above, the form has an id equal to "myform".

If you need this in many forms, you can easily turn it into a plugin:

$.fn.extend({
 trackChanges: function() {
   $(":input",this).change(function() {
      $(this.form).data("changed", true);
   });
 }
 ,
 isChanged: function() { 
   return this.data("changed"); 
 }
});

Then you can simply say:

$("#myform").trackChanges();

and check if a form has changed:

if ($("#myform").isChanged()) {
   // ...
}

In case JQuery is out of the question. A quick search on Google found Javascript implementations of MD5 and SHA1 hash algorithms. If you wanted, you could concatenate all form inputs and hash them, then store that value in memory. When the user is done. Concatenate all the values and hash again. Compare the 2 hashes. If they are the same, the user did not change any form fields. If they are different, something has been edited, and you need to call your persistence code.

Form changes can easily be detected in native JavaScript without jQuery:

function initChangeDetection(form) {
  Array.from(form).forEach(el => el.dataset.origValue = el.value);
}
function formHasChanges(form) {
  return Array.from(form).some(el => 'origValue' in el.dataset && el.dataset.origValue !== el.value);
}


initChangeDetection() can safely be called multiple times throughout your page's lifecycle: See Test on JSBin


For older browsers that don't support newer arrow/array functions:

function initChangeDetection(form) {
  for (var i=0; i<form.length; i++) {
    var el = form[i];
    el.dataset.origValue = el.value;
  }
}
function formHasChanges(form) {
  for (var i=0; i<form.length; i++) {
    var el = form[i];
    if ('origValue' in el.dataset && el.dataset.origValue !== el.value) {
      return true;
    }
  }
  return false;
}

I really like the contribution from Teekin above, and have implemented it.

However, I have expanded it to allow for checkboxes too using code like this:

// Gets all form elements from the entire document.
function getAllFormElements() {
    // Return variable.
    var all_form_elements = Array();

    // The form.
    var Form = document.getElementById('frmCompDetls');

    // Different types of form elements.
    var inputs = Form.getElementsByTagName('input');
    var textareas = Form.getElementsByTagName('textarea');
    var selects = Form.getElementsByTagName('select');
    var checkboxes = Form.getElementsByTagName('CheckBox');

    // We do it this way because we want to return an Array, not a NodeList.
    var i;
    for (i = 0; i < inputs.length; i++) {
        all_form_elements.push(inputs[i]);
    }
    for (i = 0; i < textareas.length; i++) {
        all_form_elements.push(textareas[i]);
    }
    for (i = 0; i < selects.length; i++) {
        all_form_elements.push(selects[i]);
    }
    for (i = 0; i < checkboxes.length; i++) {
        all_form_elements.push(checkboxes[i]);
    }
    return all_form_elements;
}

// Sets the initial values of every form element.
function setInitialFormValues() {
    var inputs = getAllFormElements();
    for (var i = 0; i < inputs.length; i++) {
        if(inputs[i].type != "checkbox"){
            initial_values.push(inputs[i].value);
        }
        else
        {
            initial_values.push(inputs[i].checked);
        }
    }
    
}

function hasFormChanged() {
    var has_changed = false;
    var elements = getAllFormElements();
    var diffstring = ""
    for (var i = 0; i < elements.length; i++) {
        if (elements[i].type != "checkbox"){
            if (elements[i].value != initial_values[i]) {
                has_changed = true;
                //diffstring = diffstring + elements[i].value+" Was "+initial_values[i]+"\n";
                break;
            }
         }
         else
         {
            if (elements[i].checked != initial_values[i]) {
                has_changed = true;
                //diffstring = diffstring + elements[i].value+" Was "+initial_values[i]+"\n";
                break;
            }
         }
    }
    //alert(diffstring);
    return has_changed;
}

The diffstring is just a debugging tool

Related