Detection of unsaved changes

I have a requirement to implement the "Unrelated Changes" prompt in an ASP.Net application. If the user changes the controls in the web form and tries to leave before saving, a message appears stating that they have unsaved changes and allow them to cancel and stay on the current page. The invitation should not be displayed if the user has not touched any controls.

Ideally, I would like to implement this in JavaScript, but before I went down the path of my own code, are there any existing frameworks or recommended design patterns for this? Ideally, I would like something that can be easily used across multiple pages with minimal changes.

+79
javascript prompt
01 Oct '08 at 0:41
source share
15 answers

Using jQuery:

var _isDirty = false; $("input[type='text']").change(function(){ _isDirty = true; }); // replicate for other input types and selects 

Combine with onunload / onbeforeunload methods onunload onbeforeunload .

From the comments, the following links to all input fields, without duplicate code:

 $(':input').change(function () { 

Using $(":input") applies to all input elements, textarea, select and button.

+83
01 Oct '08 at 1:26
source share

One piece of the puzzle:

 /** * 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} <code>true</code> if the form is dirty, <code>false</code> * otherwise. */ function formIsDirty(form) { for (var i = 0; i < form.elements.length; i++) { var element = form.elements[i]; var type = element.type; if (type == "checkbox" || type == "radio") { if (element.checked != element.defaultChecked) { return true; } } else if (type == "hidden" || type == "password" || type == "text" || type == "textarea") { if (element.value != element.defaultValue) { return true; } } else if (type == "select-one" || type == "select-multiple") { for (var j = 0; j < element.options.length; j++) { if (element.options[j].selected != element.options[j].defaultSelected) { return true; } } } } return false; } 

And one more :

 window.onbeforeunload = function(e) { e = e || window.event; if (formIsDirty(document.forms["someForm"])) { // For IE and Firefox if (e) { e.returnValue = "You have unsaved changes."; } // For Safari return "You have unsaved changes."; } }; 

Wrap it all up, and what do you get?

 var confirmExitIfModified = (function() { function formIsDirty(form) { // ...as above } return function(form, message) { window.onbeforeunload = function(e) { e = e || window.event; if (formIsDirty(document.forms[form])) { // For IE and Firefox if (e) { e.returnValue = message; } // For Safari return message; } }; }; })(); confirmExitIfModified("someForm", "You have unsaved changes."); 

You probably also want to change the registration of the beforeunload event beforeunload to use the LIBRARY_OF_CHOICE event LIBRARY_OF_CHOICE .

+41
Oct 01 '08 at 1:09
source share

On the .aspx page, you need a Javascript function to tell if the form information is dirty

 <script language="javascript"> var isDirty = false; function setDirty() { isDirty = true; } function checkSave() { var sSave; if (isDirty == true) { sSave = window.confirm("You have some changes that have not been saved. Click OK to save now or CANCEL to continue without saving."); if (sSave == true) { document.getElementById('__EVENTTARGET').value = 'btnSubmit'; document.getElementById('__EVENTARGUMENT').value = 'Click'; window.document.formName.submit(); } else { return true; } } } </script> <body class="StandardBody" onunload="checkSave()"> 

and in the code, add triggers to the input fields, as well as a reset on the send / cancel buttons ....

 btnSubmit.Attributes.Add("onclick", "isDirty = 0;"); btnCancel.Attributes.Add("onclick", "isDirty = 0;"); txtName.Attributes.Add("onchange", "setDirty();"); txtAddress.Attributes.Add("onchange", "setDirty();"); //etc.. 
+17
01 Oct '08 at 0:51
source share

The following browser uses the onbeforeunload and jquery functions to capture any onchange event. IT is also looking for submit or reset buttons for reset, a flag indicating changes.

 dataChanged = 0; // global variable flags unsaved changes function bindForChange(){ $('input,checkbox,textarea,radio,select').bind('change',function(event) { dataChanged = 1}) $(':reset,:submit').bind('click',function(event) { dataChanged = 0 }) } function askConfirm(){ if (dataChanged){ return "You have some unsaved changes. Press OK to continue without saving." } } window.onbeforeunload = askConfirm; window.onload = bindForChange; 
+9
Nov 16 '09 at 19:35
source share

Thanks for all the answers. I finished implementing the solution using the jQuery and Protect-Data plugin. This allows you to automatically apply monitoring to all the controls on the page.

However, there are a few caveats, especially when working with an ASP.Net application:

  • When the user selects the cancel option, the doPostBack function throws a JavaScript error. I had to manually set a try-catch around the .submit call inside doPostBack in order to suppress it.

  • On some pages, the user can perform an action that performs a postback to the same page but does not save. This leads to any reloading of the JavaScript logic, so he thinks that after the postback nothing has changed when something could be. I had to implement a hidden text field that is sent back with the page and used to store a simple boolean value indicating whether the data is dirty. This is retained after the postback.

  • You may want some backlinks on the page to not cause a dialog, such as the "Save" button. In this case, you can use jQuery to add the OnClick function, which sets window.onbeforeunload to null.

Hope this helps everyone who needs to implement something like this.

+7
01 Oct '08 at 23:45
source share

The following solution works for the prototype (tested in FF, IE 6 and Safari). It uses a common form observer (which forms the form: changes when any form fields have changed), which you can also use for other things.

 /* use this function to announce changes from your own scripts/event handlers. * Example: onClick="makeDirty($(this).up('form'));" */ function makeDirty(form) { form.fire("form:changed"); } function handleChange(form, event) { makeDirty(form); } /* generic form observer, ensure that form:changed is being fired whenever * a field is being changed in that particular for */ function setupFormChangeObserver(form) { var handler = handleChange.curry(form); form.getElements().each(function (element) { element.observe("change", handler); }); } /* installs a form protector to a form marked with class 'protectForm' */ function setupProtectForm() { var form = $$("form.protectForm").first(); /* abort if no form */ if (!form) return; setupFormChangeObserver(form); var dirty = false; form.observe("form:changed", function(event) { dirty = true; }); /* submitting the form makes the form clean again */ form.observe("submit", function(event) { dirty = false; }); /* unfortunatly a propper event handler doesn't appear to work with IE and Safari */ window.onbeforeunload = function(event) { if (dirty) { return "There are unsaved changes, they will be lost if you leave now."; } }; } document.observe("dom:loaded", setupProtectForm); 
+6
May 25 '09 at 9:17
source share

General solution Support for multiple forms on this page ( Just copy and paste into your project )

 $(document).ready(function() { $('form :input').change(function() { $(this).closest('form').addClass('form-dirty'); }); $(window).bind('beforeunload', function() { if($('form:not(.ignore-changes).form-dirty').length > 0) { return 'You have unsaved changes, are you sure you want to discard them?'; } }); $('form').bind('submit',function() { $(this).closest('form').removeClass('form-dirty'); return true; }); }); 

Note. This solution is combined with other solutions to create a common integrated solution.

Features:

  • Just copy and paste into your application.
  • Support for multiple forms.
  • You can create or do actions with dirty forms, since they have a form-dirty class.
  • You can exclude some forms by adding a ignore-change class.
+6
May 21 '15 at 12:57
source share

Here is a simple javascript / jquery solution. It takes into account the "cancellation" of the user, he is encapsulated in a function for the convenience of the application, and he does not miss gaps when sending. Just call the function and pass the identifier of your form.

This function serializes the form once when the page loads, and again before the user leaves the page. If the two form states are different, a prompt is displayed.

Try: http://jsfiddle.net/skibulk/Ydt7Y/

 function formUnloadPrompt(formSelector) { var formA = $(formSelector).serialize(), formB, formSubmit = false; // Detect Form Submit $(formSelector).submit( function(){ formSubmit = true; }); // Handle Form Unload window.onbeforeunload = function(){ if (formSubmit) return; formB = $(formSelector).serialize(); if (formA != formB) return "Your changes have not been saved."; }; } $(function(){ formUnloadPrompt('form'); }); 
+5
Oct 22 '13 at 18:36
source share

I recently contributed to an open source jQuery plugin called dirtyForms .

The plugin is designed to work with dynamically added HTML, support for multiple forms, support for almost any dialog structure, return to the beforeunload dialog box, has a plug-in supporting infrastructure to support getting dirty status from user editors (tinyMCE plugin is enabled), works in iFrames, and dirty status can be installed or reset as desired.

https://github.com/snikch/jquery.dirtyforms

+4
Apr 30 '12 at 19:35
source share

Detecting form changes using jQuery is very simple:

 var formInitVal = $('#formId').serialize(); // detect form init value after form is displayed // check for form changes if ($('#formId').serialize() != formInitVal) { // show confirmation alert } 
+4
Dec 15 '14 at 10:48
source share

I expanded the Slace suggestion above to include most of the editable elements, as well as excluding some elements (with a CSS style called "srSearch" here) due to the dirty flag being set.

 <script type="text/javascript"> var _isDirty = false; $(document).ready(function () { // Set exclude CSS class on radio-button list elements $('table.srSearch input:radio').addClass("srSearch"); $("input[type='text'],input[type='radio'],select,textarea").not(".srSearch").change(function () { _isDirty = true; }); }); $(window).bind('beforeunload', function () { if (_isDirty) { return 'You have unsaved changes.'; } }); 

+2
Feb 14 '12 at 5:17
source share
  var unsaved = false; $(":input").change(function () { unsaved = true; }); function unloadPage() { if (unsaved) { alert("You have unsaved changes on this page. Do you want to leave this page and discard your changes or stay on this page?"); } } 

window.onbeforeunload = unloadPage;

+2
Jul 08 '14 at 6:10
source share

This is exactly what the Fleegix.js fleegix.form.diff ( http://js.fleegix.org/plugins/form/diff ). Serialize the initial state of the form when loading with fleegix.form.toObject ( http://js.fleegix.org/ref#fleegix.form.toObject ) and save it in a variable, and then compare it with the current state using fleegix.form.diff when unloading. Easy as a pie.

+1
Oct 02 '08 at 1:32
source share

One method is using arrays to store variables so that changes can be tracked.

Here's a very simple way to detect changes , but the rest is not so elegant.

Another method, which is pretty simple and small, from Farfetched Blog :

 <body onLoad="lookForChanges()" onBeforeUnload="return warnOfUnsavedChanges()"> <form> <select name=a multiple> <option value=1>1 <option value=2>2 <option value=3>3 </select> <input name=b value=123> <input type=submit> </form> <script> var changed = 0; function recordChange() { changed = 1; } function recordChangeIfChangeKey(myevent) { if (myevent.which && !myevent.ctrlKey && !myevent.ctrlKey) recordChange(myevent); } function ignoreChange() { changed = 0; } function lookForChanges() { var origfunc; for (i = 0; i < document.forms.length; i++) { for (j = 0; j < document.forms[i].elements.length; j++) { var formField=document.forms[i].elements[j]; var formFieldType=formField.type.toLowerCase(); if (formFieldType == 'checkbox' || formFieldType == 'radio') { addHandler(formField, 'click', recordChange); } else if (formFieldType == 'text' || formFieldType == 'textarea') { if (formField.attachEvent) { addHandler(formField, 'keypress', recordChange); } else { addHandler(formField, 'keypress', recordChangeIfChangeKey); } } else if (formFieldType == 'select-multiple' || formFieldType == 'select-one') { addHandler(formField, 'change', recordChange); } } addHandler(document.forms[i], 'submit', ignoreChange); } } function warnOfUnsavedChanges() { if (changed) { if ("event" in window) //ie event.returnValue = 'You have unsaved changes on this page, which will be discarded if you leave now. Click "Cancel" in order to save them first.'; else //netscape return false; } } function addHandler(target, eventName, handler) { if (target.attachEvent) { target.attachEvent('on'+eventName, handler); } else { target.addEventListener(eventName, handler, false); } } </script> 
0
01 Oct '08 at 0:56
source share

In IE, document.ready will not work properly, it will update input values.

so we need to bind the load event inside the document.ready function, which will also handle the IE browser.

below is the code you have to enter inside the document.ready function.

  $(document).ready(function () { $(window).bind("load", function () { $("input, select").change(function () {}); }); }); 
0
Jul 06 '15 at 6:04
source share



All Articles