On ASP.Net / AJAX recent project I was asked to ensure that users cannot navigate away from a web form if they have made changes that have not yet been submitted.
The requirements were:
- Enable users to submit the form by clicking the “Save” or “Cancel” buttons.
- When user navigates away using any other method, show a dialog that states that changes will be lost if the user continues. These methods to include:
- Any other controls on the page
- Back button
- Refresh button
- Typing another address in the address bar
- Picking another page from history
- Closing the web browser
- Anything else
- Only show the dialog if the form is “dirty” (ie. there are unsaved changes”
- Work with IE6 (I know, I know).
Note that his solution has not been tested with other browsers.
Preventing Users Navigating
When a user attempts to navigate using any of the methods above, the window.onbeforeunload event will fire. We can use this event to pop-up a dialog that warns the user that she has not submitted her changes, and asks her to confirm that she wants to continue.
Checking for Dirt
In order to check for dirtiness, we must compare the original value of all the controls on the page with their value when they are submitted. A number of sources suggest that the browser maintains the default value for controls, and that we can compare the current value with that default value in the onbeforeunload event handler. However, there are several issues with this approach:
- The default value is generally reset following an AJAX postback. This means that we must preserve the control’s original values of the controls when they first load, so gain nothing from using the browser’s method for tracking value changes.
- In IE6, the defaultSelected property of HTML OPTION controls is, by default, set to true for all items in a drop-down list. The ASP.Net list control appears to set this value explicitly, I didn’t want to have to do so on the equivalent HTML control. As a result of the default, however, it is often impossible to determine if the user has unselected an item or if they have simply not selected the item. As a result it is best to track changes to the selection of options ourselves, rather than using the browser’s faulty tracking mechanism.
Allowing Certain Controls to Submit without Warning
It is important that some controls are able to submit the form without the using being warned that changes have been made. Typically, these controls include a button that allows the user to save her changes, and possibly one to exit without saving.
In order to implement these, we set a flag that indicates that a dirtiness check is required, then use the client-side click event of the buttons to reset the flag so that the check isn’t performed when these buttons are clicked.
OnBeforeUnload Sometimes Fires Twice
There is a bug in IE6 that means that, under some circumstances, the onbeforeunload event fires twice. We don’t want the confirm dialog to be displayed twice when the user attempts to leave a page, so we need to take measures. It is easy to set a flag the first time the onbeforeunload event fires that indicates that we can ignore it the second time. The slight complication is that the user may click “cancel” to prevent the navigation when the dialog first appears. If that happens, we don’t want to suppress the confirmation dialog for subsequent attempts to navigate away from the page. The solution is to start a timer that will reset the flag if the user remains on the page, thus allowing the dialog to appear when the user attempts to navigate again.
The source code for my Save and Cancel buttons looks like this:
<asp:Button ID="btnEdit" runat="server" Text="Save" OnClick="btnEdit_Click" OnClientClick="ignoreDirty();" /> <asp:Button ID="btnCancel" runat="server" Text="Cancel" CssClass="btn btnCancel" OnClick="btnCancel_Click" OnClientClick="ignoreDirty();" CausesValidation="false" />
I looked at a range of solutions before developing the one I present here, including: