jQuery Method To Prompt A User To Save Changes Before Leaving Page
Posted By : todd sharp Posted At : October 12, 2009 12:58 PM Posted In: jQuery
14
I'm doing a little work on cleaning up an application and came acrossed an undesirable circumstance related to the UI. The app contains what appears to be a tab navigator, but rather then the tabbed links revealing hidden form elements in another tab they take the user to a whole new page. The obvious problem with this design is that the user may populate elements in the first 'pseudo-tab' and click on the next tab assuming that they will be able to save the data after populating the other tab. Since I'm not at liberty to make massive changes to the UI I came up with the following solution using jQuery so the user is at least warned when they leave without saving their changes. I think it's a workable solution, and better yet it was terribly easy to do.
The first step here is to know when the form is 'dirty'. That is, when a user has made a change to the form that has not been saved. There are a number of ways to determine that a form is dirty, but I chose to declare a global variable called 'isDirty':
Note: Please see the update below for a better method using the onbeforeunload event of the window.
The next step is to update the dirty flag when a user has made a change. When the DOM is ready I simply register a 'keyup' 'change' listener for all form inputs that will update the dirty flag as needed. Note: the ':input' selector will handle all form inputs, including <select> and <textarea> tags.
if(!isDirty){
isDirty = true;
}
});
Simple enough, right? Finally, I want to capture when the user clicks on a link that would navigate them away from the current page.
if(isDirty){
var confirmExit = confirm('Are you sure? You haven\'t saved your changes. Click OK to leave or Cancel to go back and save your changes.');
if(confirmExit){
return true;
}
else{
return false;
}
}
});
Here we assign a click handler for every <a> element on the page. If the form is dirty we display a confirm dialog that warns them of the unsaved changes. If they confirm that they want to discard the changes we return true (allowing the normal action on the click event), else we return false (interrupting the normal link action). Since the pages save action will post the form there is no need to reset the dirty flag anywhere else, but if the save action was handled with Ajax you'd obviously want to reset the dirty flag.
I should mention that this solution is not fail proof. Any JavaScript methods that use 'window.location' to navigate will not be caught. Also, if the user closes the browser window they will not be prompted to save their changes. I may look into capturing the window unload event to handle that case, but memory tells me that the window unload event is pretty hard to catch in a reliable cross browser way.
To recap here is the entire example:
var isDirty = false;
$(document).ready(function(){
$(':input').change(function(){
if(!isDirty){
isDirty = true;
}
});
$('a').click(function(){
if(isDirty){
var confirmExit = confirm('Are you sure? You haven\'t saved your changes. Click OK to leave or Cancel to go back and save your changes.');
if(confirmExit){
return true;
}
else{
return false;
}
}
});
});
</script>
Update: It seems assigning a function to the window.onbeforeunload event works pretty well. Here is the simplified and revised function that should catch all attempts at leaving the page (closing the window, following links, etc):
var msg = 'You haven\'t saved your changes.';
$(document).ready(function(){
$(':input').change(function(){
if(!isDirty){
isDirty = true;
}
});
window.onbeforeunload = function(){
if(isDirty){
return msg;
}
};
});



You need to save the original form values and compare them before saving. I found this solution: http://code.google.com/p/jquery-form-observe/ that monitors the form and highlights fields that have been changed.
However check in the code as the poll interval is set to 1ms which really puts some load on slower CPUs - change it to 500ms or more.
@Mike - good catch. Let me test that out and I'll update the post.
Actually, the keyup is bad now that I think of it - because just tabbing thru fields will mark the form as dirty. I think I'll change it to the change event like you suggested.
I wish I had Flex's valueCommit event in JavaScript.
On page load use $(yourForm).serialize();
On submit, re-serialize the form and see if it is any different, then handle that as required.
This is much shorter / more maintainable (you can add / remove fields from the form without having to touch this code).
I'll just add that I found it useful to check the type of the target element so to avoid the pop-up when the user actually uses your form's submit button.
window.onbeforeunload = function(e)
{
var _target = $(e.explicitOriginalTarget);
if (window.is_dirty && _target.length && !_target.is(':input'))
{
return msg;
}
};
I'm using the avoid popup on submit button part from n0nick dated jan 4. I cannot seem to get it to work in IE6 (yes IE6 .. our IT is planning upgrade to IE8 in a couple months). It works great in FF. I tried changing the IF statement to:
(window.isDirty && _target.length && !_target.is(':input'))
((window.isDirty || isDirty) && _target.length && !_target.is(':input'))
and a combo of is_dirty in there too and nothing triggers with this.
The original format works, however, I know I will get complaints if it pops up on submit also. Does anyone know how I can fix this so it works in IE6?