General way to detect html form change

I have a tabbed HTML form. When switching from one tab to another, the current tab data is saved (on the database), even if there are no changes in the data.

I would like to make a persistence call only if the form is edited. The form may contain any kind of control. Contamination of the form is not necessary by typing, but the date selection in the calendar control will also qualify.

One way to achieve this is to display the form in read-only mode by default and click the "Edit" button, and if the user clicks the edit button, then a call will be made to the database (again, regardless of whether the data is changed. This is the best improving what currently exists).

I would like to know how to write a generic javascript function that will check if any control value has been changed?

+74
javascript jquery html
Jun 06 '09 at 13:28
source share
7 answers

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

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

Then before saving you can check if it has been changed:

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

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

If you need it 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 just say:

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

and check if the form has changed:

 if ($("#myform").isChanged()) { // ... } 
+132
Jun 06 '09 at 13:32
source share

In case jquery is out of the question. A quick Google search revealed the Javascript implementation of the MD5 and SHA1 hash algorithms. If you want, you can combine all the inputs of the forms and hash them, and then store this value in memory. When the user is done. Combine all values ​​and hash again. Compare 2 hashes. If they match, the user has not changed any form fields. If they differ from each other, something has been edited, and you need to call the persistence code.

+32
Jun 06 '09 at 13:43
source share

I'm not sure if I got your question right, but what about addEventListener? If you don't care about IE8 support, this should be fine. The following code works for me:

 var form = document.getElementById("myForm"); form.addEventListener("input", function () { console.log("Form has changed!"); }); 
+11
Jun 30 '16 at 2:07
source share

Here is how I did it (without using jQuery).

In my case, I wanted one particular form element not to be taken into account, because it was the element that caused the validation, and therefore will always be changed. The exclusive element is called "report_period" and is hardcoded in the "hasFormChanged ()" function.

To check, make an element that calls the "changeReportingPeriod ()" function, which you probably want to call something else.

IMPORTANT: you must call setInitialValues ​​() when the values ​​were set to their original values ​​(usually when the page loads, but not in my case).

NOTE. I am not saying that this is an elegant solution; in fact, I do not believe in elegant JavaScript solutions. My personal emphasis in JavaScript is on readability, not structural elegance (as if it were possible in JavaScript). I don’t really care about file size when writing JavaScript, because what gzip is for, and trying to write more compact JavaScript code invariably leads to unbearable maintenance problems. I do not apologize, express remorse and refuse to discuss it. This is JavaScript. Sorry, I had to make this clear in order to convince myself that I should worry about publishing. Be happy!:)

 var initial_values = new Array(); // Gets all form elements from the entire document. function getAllFormElements() { // Return variable. var all_form_elements = Array(); // The form. var form_activity_report = document.getElementById('form_activity_report'); // Different types of form elements. var inputs = form_activity_report.getElementsByTagName('input'); var textareas = form_activity_report.getElementsByTagName('textarea'); var selects = form_activity_report.getElementsByTagName('select'); // 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]); } 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++) { initial_values.push(inputs[i].value); } } function hasFormChanged() { var has_changed = false; var elements = getAllFormElements(); for (var i = 0; i < elements.length; i++) { if (elements[i].id != 'reporting_period' && elements[i].value != initial_values[i]) { has_changed = true; break; } } return has_changed; } function changeReportingPeriod() { alert(hasFormChanged()); } 
+4
Aug 26 '10 at 21:40
source share

Another way to achieve this is to serialize the form:

 $(function() { var $form = $('form'); var initialState = $form.serialize(); $form.submit(function (e) { if (initialState === $form.serialize()) { console.log('Form is unchanged!'); } else { console.log('Form has changed!'); } e.preventDefault(); }); }); 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <form> Field 1: <input type="text" name="field_1" value="My value 1"> <br> Field 2: <input type="text" name="field_2" value="My value 2"> <br> Check: <input type="checkbox" name="field_3" value="1"><br> <input type="submit"> </form> 
+4
Jul 31 '17 at 19:18
source share

Shape changes can be easily 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 be safely called several times throughout the life cycle of your page: see the "Testing in JSBin" section.




For older browsers that do not support the new 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; } 
+3
01 Oct '18 at 14:19
source share

Here is an example of a polyfill method in native JavaScript that uses the FormData() API to detect created, updated and deleted form entries. You can check if something has been changed using HTMLFormElement#isChanged and get an object that contains differences from the reset form. using HTMLFormElement#changes (if they are not masked by the input name):

 Object.defineProperties(HTMLFormElement.prototype, { isChanged: { configurable: true, get: function isChanged () { 'use strict' var thisData = new FormData(this) var that = this.cloneNode(true) // avoid masking: https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/reset HTMLFormElement.prototype.reset.call(that) var thatData = new FormData(that) const theseKeys = Array.from(thisData.keys()) const thoseKeys = Array.from(thatData.keys()) if (theseKeys.length !== thoseKeys.length) { return true } const allKeys = new Set(theseKeys.concat(thoseKeys)) function unequal (value, index) { return value !== this[index] } for (const key of theseKeys) { const theseValues = thisData.getAll(key) const thoseValues = thatData.getAll(key) if (theseValues.length !== thoseValues.length) { return true } if (theseValues.some(unequal, thoseValues)) { return true } } return false } }, changes: { configurable: true, get: function changes () { 'use strict' var thisData = new FormData(this) var that = this.cloneNode(true) // avoid masking: https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/reset HTMLFormElement.prototype.reset.call(that) var thatData = new FormData(that) const theseKeys = Array.from(thisData.keys()) const thoseKeys = Array.from(thatData.keys()) const created = new FormData() const deleted = new FormData() const updated = new FormData() const allKeys = new Set(theseKeys.concat(thoseKeys)) function unequal (value, index) { return value !== this[index] } for (const key of allKeys) { const theseValues = thisData.getAll(key) const thoseValues = thatData.getAll(key) const createdValues = theseValues.slice(thoseValues.length) const deletedValues = thoseValues.slice(theseValues.length) const minLength = Math.min(theseValues.length, thoseValues.length) const updatedValues = theseValues.slice(0, minLength).filter(unequal, thoseValues) function append (value) { this.append(key, value) } createdValues.forEach(append, created) deletedValues.forEach(append, deleted) updatedValues.forEach(append, updated) } return { created: Array.from(created), deleted: Array.from(deleted), updated: Array.from(updated) } } } }) document.querySelector('[value="Check"]').addEventListener('click', function () { if (this.form.isChanged) { console.log(this.form.changes) } else { console.log('unchanged') } }) 
 <form> <div> <label for="name">Text Input:</label> <input type="text" name="name" id="name" value="" tabindex="1" /> </div> <div> <h4>Radio Button Choice</h4> <label for="radio-choice-1">Choice 1</label> <input type="radio" name="radio-choice-1" id="radio-choice-1" tabindex="2" value="choice-1" /> <label for="radio-choice-2">Choice 2</label> <input type="radio" name="radio-choice-2" id="radio-choice-2" tabindex="3" value="choice-2" /> </div> <div> <label for="select-choice">Select Dropdown Choice:</label> <select name="select-choice" id="select-choice"> <option value="Choice 1">Choice 1</option> <option value="Choice 2">Choice 2</option> <option value="Choice 3">Choice 3</option> </select> </div> <div> <label for="textarea">Textarea:</label> <textarea cols="40" rows="8" name="textarea" id="textarea"></textarea> </div> <div> <label for="checkbox">Checkbox:</label> <input type="checkbox" name="checkbox" id="checkbox" /> </div> <div> <input type="button" value="Check" /> </div> </form> 
0
Jul 31 '17 at 20:35
source share



All Articles