In my ASP.NET web app, I'm trying to create a universal way of warning users before navigating away from a form when they've made changes, using jQuery. Pretty standard stuff, but after a lot of searching I have yet to find a technique that works.
Here's what I have at this point:
addToPostBack = function(func) {
var old__doPostBack = __doPostBack;
if (typeof __doPostBack != 'function') {
__doPostBack = func;
} else {
__doPostBack = function() {
old__doPostBack();
func();
}
}
}
var isDirty = false;
$(document).ready(function() {
addToPostBack(function() {
alert("Postback detected.")
clearDirty();
});
$(':input').bind("change select keydown", setDirty);
window.onbeforeunload = function(e) {
var msg = "You have unsaved changes. "
if (isDirty == true) {
var e = e || window.event;
if (e) { e.returnValue = msg; }
return msg;
}
};
});
setDirty = function() {isDirty = true;}
clearDirty = function() {isDirty = false;}
This works as far as warning the user from navigating away. The problem is that I get the warning on every same-page postback. There are a number of things on my forms that might trigger a postback:
None of these things should provoke a warning, because the user isn't leaving the page. My code tries to hijack addToPostBack (more details on that in this question) to clear the isDirty bit before posting back, but the problem is that in IE onbeforeunload
fires before __doPostBack, apparently because IE fires onbeforeunload immediately when a link is clicked (as described here).
Of course, I could wire up each of these controls to clear the isDirty bit, but I'd prefer a solution that operates on the form level and that doesn't require that I touch every single control that might trigger a postback.
Does anyone have an approach that works in ASP.NET and that doesn't involve wiring up every control that might cause a postback?
I came across this post while Googling for a solution for doing the same thing in MVC. This solution, adapted from Herb's above, seems to work well. Since there's nothing MVC-specific about this, it should work just as well for PHP, Classic ASP, or any other kind of application that uses HTML and JQuery.
var isDirty = false;
$(document).ready(function () {
$(':input').bind("change select keydown", setDirty);
$('form').submit(clearDirty);
window.onbeforeunload = function (e) {
var msg = "You have unsaved changes. "
if (isDirty == true) {
var e = e || window.event;
if (e) { e.returnValue = msg; }
return msg;
}
};
});
setDirty = function () { isDirty = true; }
clearDirty = function () { isDirty = false; }
Interesting, but... why don't you do everything with jQuery?
var defaultSubmitControl = false;
var dirty = false;
$(document).ready(function( ) {
$('form').submit(function( ) { dirty = false });
$(window).unload(function( ) {
if ( dirty && confirm('Save?') ) {
__doPastBack(defaultSubmitControl || $('form :submit[id]').get(0).id, '');
}
});
});
···
dirty = true;
···
Now, if that still causes the same issue (unload
triggering before submit
), you could try a different event tree, so instead of calling __doPostBack
directly you do...
setTimeout(function( ) {
__doPastBack(defaultSubmitControl || $('form :submit[id]').get(0).id, '');
}, 1); // I think using 0 (zero) works too
I haven't tried this and it's from the top of my head, but I think it could be a way to solve it.
You could always create an inherited page class that has a custom OnLoad
/ OnUnload
method that adds in immediate execution JavaScript.
Then you don't have to handle it at a control specific level but rather the form / page level.
Got this to work by basically tracking the mouse position. Keep in mind you can still get positive values to your Y value (hence my < 50 line of code), but as long as your submit buttons are more than 100 pixels down you should be fine.
Here is the Javascript I added to track mouse changes and capture the onbeforeunload event:
<script language="JavaScript1.2">
<!--
// Detect if the browser is IE or not.
// If it is not IE, we assume that the browser is NS.
var IE = document.all?true:false
// If NS -- that is, !IE -- then set up for mouse capture
if (!IE) document.captureEvents(Event.MOUSEMOVE)
// Set-up to use getMouseXY function onMouseMove
document.onmousemove = getMouseXY;
// Temporary variables to hold mouse x-y pos.s
var tempX = 0
var tempY = 0
// Main function to retrieve mouse x-y pos.s
function getMouseXY(e) {
if (IE) { // grab the x-y pos.s if browser is IE
tempX = event.clientX + document.body.scrollLeft
tempY = event.clientY + document.body.scrollTop
} else { // grab the x-y pos.s if browser is NS
tempX = e.pageX
tempY = e.pageY
}
// catch possible negative values in NS4
if (tempX < 0){tempX = 0}
if (tempY < 0){tempY = 0}
// show the position values in the form named Show
// in the text fields named MouseX and MouseY
document.Show.MouseX.value = tempX
document.Show.MouseY.value = tempY
return true
}
//-->
</script>
<script type="text/javascript">
window.onbeforeunload = HandleOnClose;
function HandleOnClose(e) {
var posY = 0;
var elem = document.getElementsByName('MouseY');
if (elem[0]) {
posY = elem[0].value;
}
if (posY < 50) { // Your form "submit" buttons will hopefully be more than 100 pixels down due to movement
return "You have not selected an option, are you sure you want to close?";
}
}
</script>
Then just add the following form onto your page:
<form name="Show">
<input type="hidden" name="MouseX" value="0" size="4">
<input type="hidden" name="MouseY" value="0" style="display:block" size="0">
</form>
And that's it! It could use a little cleanup (remove the MouseX, etc), but this worked in my existing ASP.net 3.5 application and thought I would post to help anyone out. Works in IE 7 and Firefox 3.6, haven't tried Chrome yet.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With