Click here to Skip to main content
Click here to Skip to main content

Work smarter not harder: “Navigate Away” feature for your ASP.NET/other web applications made easy

, 14 Dec 2010
Rate this:
Please Sign up or sign in to vote.
A jQuery script that lets you display the “Navigate Away” browser message when an input field value is changed in the page, and for that, you don’t need to write any code!

Introduction

While you browse and surf through the net, you must have seen a message somewhere similar to the following:

A sample navigate away message in the browser

You know when this happens. The above message appears if you are in the middle of doing some changes in the current web page (changing form data) and you are about to leave the page for some reason without submitting the changes. Simple.

But, as a developer, this might not be an easy feature for you to implement. At first glance, it looks as if you have to track the changes of every little form element value and show a message if one has changed. To make the situation tougher, not every page has the same set of input elements. So, tracking change for each page might get even tougher, and each page might require implementing its own change tracking logic.

Frankly speaking, when I first encountered such a requirement, I thought, "oh, there must be some built-in feature in the browsers". The browser should be able to track changes of the input elements of the currently displayed page (everything is HTML and each object is available in the DOM, right?). So all I might need to know is to just enable the feature.

Sadly, I was wrong. Browsers don't have such a built-in feature (I still believe they should have), and hence, it is us (poor developers) who have to track the changes and show those lovely(?) messages.

To implement this feature, most of the time, we may end up writing code for tracking changes in each different page (because each page may have a different set of input elements), or may be, we might implement some logic to track the changes of the input elements in a harder way (not in a smarter way).

So, even if I am not a JavaScript or jQuery expert, I thought about trying to develop something in a generic way that would allow us to implement the "Navigate Away" feature for an ASP.NEt web application (and probably for any web platform, be it ASP.NET or PHP) without writing any actual code! This article is an effort to demonstrate this generic implementation.

(Yes, there may be one or two Navigate Away jQuery plug-ins appearing in your Google search, but I found those to be buggy or not meeting the exact requirements.)

The requirement

  • If the user modifies any value in any input field in a web form (page), and if he/she performs anything that requires leaving the page (clicking a hyperlink, closing the page, etc.), the web page should show a message that states he/she may lose the changes if they navigate away.
  • If the user modifies values in input fields and then reverts the value changes for some reason, and then if he/she performs anything that requires leaving the page, the web page should not show any navigate away message (because no value has actually changed).
  • If the user submits a form or performs some actions (which may differ from page to page), the web page should not display any "Navigate Away" message even if input element values have changed. In each page, it should be easy to configure which actions should not cause showing a navigate away message.
  • It should be easy for any page to not use the "Navigate Away" feature.
  • The page developer should not be required to write any code for implementing the "Navigate Away" feature. The implementation should be developed in a "plug-in" fashion.
  • The plug-in implementation logic should be simple (this shouldn't be rocket science).

Sample implementation

I've developed a jQuery script for implementing the "Navigate Away" feature and used this in a sample ASP.NET application, which you can download from above:

Sample ASP.NET web site that uses the "Navigate Away" script

The sample application is an ASP.NET web site which contains the following things:

  • "navigateaway.js" is the jQuery script that this article is all about, and "navigateaway.min.js" is the minified version of the same file.
  • MasterPage.master is where the NavigateAway.js script is used as follows:
<script language="javascript" src="js/jquery.js"></script>  
<script language="javascript" src="js/navigateaway.min.js"></script>

The Default.aspx uses the Navigate Away feature, and to do so, it doesn't have to do anything (because it includes the MasterPage.master). The Default2.aspx also includes the Master page, but it doesn't use the Navigate Away feature. To not to use the feature, it just calls the following JavaScript method:

<script language="javascript">
       DisableNavigateAway();
</script>

To have a look at the sample application, browse Default.aspx of the sample application. You will see the following screen output:

Default.png

Page output of Default.aspx

The page contains the following input form elements:

  • A text box
  • A drop-down list
  • A check box group
  • A radio button group
  • A file selection

And it contains the following action elements, which could make the user leave the current page:

  • A hyperlink (Hyperlink)
  • A normal button (Button)
  • A submit button (Submit)

If the user changes any value in the input element of the page, the following actions should display the Navigate Away message:

  • Clicking on the hyperlink (Hyperlink)
  • Clicking on the normal button (Button)
  • Closing the page

However, the following action does not show any Navigate Away message, even if a form element value has changed:

  • Clicking on the submit button (Submit)

If user changes the input element values, and changes them back to their initial value again, the Navigate Away message is not shown.

That's it! Feel free to play with the page. Happy "Navigate Away".

How to use the NavigateAway script

To implement the "Navigate Away" feature, all you need to do is to add a reference to the script "navigateaway.min.js" (the minified version of the jQuery script) in the HTML (or XHTML markup), and most of the time, you might have the opportunity to add the reference within a single file (which is included or reused across most or all files within the application), and you are done. For example, if you are working with an ASP.NET application, including the script in the MasterPage file will be good enough for you, if all other pages in your application uses that MasterPage file.

<script language="javascript" src="js/navigateaway.min.js"></script>

If the script is included in the MasterPage or a common re-usable file, the "Navigate Away" feature will be turned on by default for every page which uses or includes the file. Now, there will be some pages where you might not want to use the "Navigate Away" feature. How should you configure this?

Fortunately, this is simple. You just need to call the following function in the HTML (or XHTML) to achieve this:

<script language="javascript">
       DisableNavigateAway();
</script>

If the "Navigate Away" feature is enabled for a particular page, it will be turned on for each and every action that results in leaving the current page for the user. This may not be desired at times. For example, you surely would not like to see the "Navigate Away" message after filling up a form and pressing the Submit button or clicking on a "Link" or "Image Link" that submits and saves the form data to the server. Also, you may not want to show the Navigate Away message if the user clicks on a button/link in a modal popup confirmation box.

The navigateaway.js script makes this extremely easy to configure. You just need to specify the class nonavigate for an element for which you would not want to fire the "Navigate Away" action.

For example, "Default.aspx" has a submit button (Submit) for which the "Navigate Away" message should not be displayed, even if an input element value in the form was modified.

Default.png

Clicking on the "Submit" button should not show the "Navigate Away" message

To configure the "Submit" button not to fire the "Navigate away" action, the class nonavigate has to be specified as follows:

<input type="submit" id="submit" class="nonavigate" value="submit" />

And, if an element in the HTML (XHTML) in the page has the nonavigate class specified, it will not fire the "Navigate Away" action, no matter the input values in the form were changed or not.

The Script

Following is the jQuery code. It is small, and is built upon an easy logic.

//Initial form value
var initialValue = '';
//Form value at page navigate away event
var userValue = '';
//Default navigate away message
var NAVIGATE_AWAY_MESSAGE = 
  "The changes you made will be lost if you navigate away from this page.";
//Flag to track whether onbeforeunload event
//is fired already. Required for IE only
var onBeforeUnloadFired = false;
//Flag to disable onbeforeunload plugin 
var DISABLE_ONBEFOREUNLOAD_PLUGIN = false;
//Flag whether source Element class causing the firing
//of OnBeforeUnload event have "nonavigate" class
var IgnoreNavigateForEventSource = false;

//Read initial form values and attach the onbeforeunload event
$(document).ready(function() {

    initialValue = GetFormValues();
    window.onbeforeunload = handleOnBeforeUnload;

    //Detect whether the event source has
    //"nonavigate" class specified
    $("a,input,img").click(function() {
        IgnoreNavigateForEventSource = $(this).hasClass("nonavigate");
    });

});

//Disables navigate away feature
function DisableNavigateAway() {
    DISABLE_ONBEFOREUNLOAD_PLUGIN = true;
}

//Sets navigate away message
function SetNavigateAwayMessage(message) {
    NAVIGATE_AWAY_MESSAGE = message;
}

//Do not show navigate away message for the specified element Id
function IgnoreNavigateAwayFor(elementId) {
    $("#" + elementId).addClass('nonavigate');
}

//Reads control values in the form
function GetFormValues() {
    var formValues = '';
    $.each($('form').serializeArray(), function(i, field) {
        if (field.name != '__EVENTVALIDATION'
        && field.name != '__EVENTTARGET'
        && field.name != '__EVENTARGUMENT'
        && field.name != '__VIEWSTATE'
        && field.name != '__VIEWSTATEENCRYPTED') {

            var inputField = $("[name=" + field.name + "]");
            var displayProperty = $(inputField).css("display");

            //Ignore the form element which have style property display="none"
            if (displayProperty != "none") {
                formValues = formValues + "-" + field.name + ":" + field.value;
            }
        }
    });

    //Read the check box element values as these are not
    //returned by the form.serializeArray() Jquery method
    $(':checkbox').each(function() {
        formValues = formValues + "-" + $(this).attr("checked");
    });

    //Read the check box element values as these are
    //not returned by the form.serializeArray() Jquery method
    $(':radio').each(function() {
        formValues = formValues + "-" + $(this).attr("checked");
    });

    //Read the check box element values as these are not
    //returned by the form.serializeArray() Jquery method
    $(':file').each(function() {
        formValues = formValues + "-" + $(this).val();
    });
    return formValues;
}

//Reset the onbeforeunload flag : Required for IE
//only as IE has a bug of firing the onbeforeunload
//event twice
function ResetOnBeforeUnloadFired() {
    onBeforeUnloadFired = false;
}

//OnBeforeUnload event handler
function handleOnBeforeUnload(event) {

    //Do not show message if plugin is disabled 
    if (DISABLE_ONBEFOREUNLOAD_PLUGIN) return;
    //Execute function if the onbeforeunload not fired already: Required
    //for IE only as IE has a bug of firing the onbeforeunload
    //event twice
    if (!onBeforeUnloadFired) {
        onBeforeUnloadFired = true;

        if (IgnoreNavigateForEventSource) {
            //Reset the flag
            IgnoreNavigateForEventSource = false;
            return;
        }
        
        //Reset the onBeforeUnloadFired flag after
        //a few milliseconds. Meanwhile, display the navigate
        //away message if the initial form value
        //is not same as current form value 

        //Resetting the onBeforeUnloadFired flag after
        //a few milliseconds ensures that if the same event is fired
        //twice (In IE), the flag will be reset by this
        //time and hence the same event handling codes will
        //not be executed
        window.setTimeout("ResetOnBeforeUnloadFired()", 10);

        userValue = GetFormValues();

        //Display navigate away message if the initial
        //form value and current form value is not the same
        if (userValue != initialValue) {
            if (NAVIGATE_AWAY_MESSAGE == "") {
                return "The changes you made will " + 
                  "be lost if you navigate away from this page.";
            }
            else {
                return NAVIGATE_AWAY_MESSAGE;
            }
        }
    }
}

The implementation logic

The functionality has been developed using a very simple logic, which is as follows:

  • Read all input element values in the form while the page loads, append the values to a JavaScript variable, and store it (say, InitialValues).
  • While leaving the current page, don't do anything if the source element (the event source element which caused the action for leaving the current page) has the class "nonavigate".
  • Otherwise, read all input element values in the form again, append the values to another JavaScript variable (say, UserValues) and compare both variable values (whether InitialValues == UserValues), and show a Navigate Away message if they are not the same.

Reading form element values

The GetFormValues() method reads the input values in the form and appends them to a variable. It basically uses a jQuery method to serialize the input form values, which is as follows:

$('form').serializeArray()

If the script is being used in an ASP.NET Web Forms application, there will be some hidden input elements within the form which we would like to filter out while trying to read and append the form's input field values. These are filtered as follows:

if (field.name != '__EVENTVALIDATION'        
&& field.name != '__EVENTTARGET'
&& field.name != '__EVENTARGUMENT'
&& field.name != '__VIEWSTATE'
&& field.name != '__VIEWSTATEENCRYPTED')

If the NavigateAway script is being used in any other platform, and if any such hidden form element is there, these needed to be filtered as above.

One limitation with the $('form').serializeArray() jQuery function is that, it doesn't return some special elements in the form, like:

  • Check box groups
  • Radio button groups
  • Files

These input element values are to be read and appended to the variables, using the following code:

//Read the check box element values as these
//are not returned by the form.serializeArray() Jquery method
$(':checkbox').each(function()
{
  formValues = formValues + "-" + $(this).attr("checked");
});
 
//Read the check box element values as these are
//not returned by the form.serializeArray() Jquery method
$(':radio').each(function()
{
  formValues = formValues + "-" + $(this).attr("checked");
});
 
//Read the check box element values as these are not returned
//by the form.serializeArray() Jquery method
$(':file').each(function()
{
  formValues = formValues + "-" + $(this).val();
});

Handling double invocation of "onbeforeunload" in the IE browser

Using jQuery, the onbeforeunload event is attached to the page load event as follows:

//Read initial form values and attach the onbeforeunload event
$(document).ready(function() {

    initialValue = GetFormValues();
    window.onbeforeunload = handleOnBeforeUnload;

    //Detect whether the event source has "nonavigate" class specified
    $("a,input,img").click(function() {
        IgnoreNavigateForEventSource = $(this).hasClass("nonavigate");
    });

});

Note that a "Click" event is also attached to each <a/>, <input/>, and <img/> element to detect whether the event source (which is currently causing the "Navigate Away" action) has the "nonavigate" class specified. If that is so, this value will be used later to determine whether or not to show the "Navigate Away" message even if the form element value is changed by the user.

Unfortunately, in IE, the onbeforeunload event is fired twice. To handle this issue, the following logic is used:

  • Use a flag to find whether the onbeforeunload event was fired already (onBeforeUnloadFired).
  • Within the handleOnBeforeUnload() method, do not do anything if onBeforeUnloadFired == true; otherwise, set onBeforeUnloadFired = true and proceed to the rest of the logic.
  • If the form input values are changed, show the Navigate Away message. Note that as onBeforeUnloadFired = true now, even if the event is fired again in IE, the Navigate Away logic will not be executed again.
  • Reset onBeforeUnloadFired = false after a certain time interval (making sure that by this time the second event fired in IE has been handled) by using the following code:
window.setTimeout("ResetOnBeforeUnloadFired()", 10);

The ResetOnBeforeUnloadFired() method is as follows:

function ResetOnBeforeUnloadFired()
{
 onBeforeUnloadFired = false;
}

Controlling the "Navigate Away" feature

Disabling "Navigate Away" for specific pages

Including the navigateaway.js script will enable the "Navigate away" feature by default. So, if the navigateaway.js script is included in a common file (say, in a MasterPage), the feature will be enabled for all of the pages using the common file. So, there has to be a way to disable the feature for a particular page, if required.

Fortunately, this is very easy. Only the following method has to be called from within the page markup:

<script language="javascript">
    DisableNavigateAway();
</script>

Ignoring the "Navigate Away" feature dynamically for an element

Setting the "nonavigate" class to an element ensures that the element will not fire the "Navigate Away" message. You may set the class nonavigate manually to the HTML/XHTML markup in your pages, or you may call the following function to achieve this:

<script language="javascript">
    IgnoreNavigateAwayFor('<%= btnSubmit.ClientID %>');
</script>

The IgnoreNavigateAwayFor() method simply adds the class nonavigate to an element dynamically. The method is defined as follows:

//Do not show navigate away message for the specified element Id
function IgnoreNavigateAwayFor(elementId) {
    $("#" + elementId).addClass('nonavigate');
}

Setting a custom Navigate Away message

By default, a generic common message is shown in the "Navigate Away" message. If required, this message could be customized as follows:

<script language="javascript">
    SetNavigateAwayMessage("Custom Navigate Away message");
</script>

Give it a try, and let me know about any feedback or improvement suggestions. I'd love to hear what you say!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Al-Farooque Shubho
Founder DropCue, SmartAspects
Bangladesh Bangladesh
I write codes to make life easier, and that pretty much describes me.
 
Sorry for not being able to contribute to CodeProject these days. I'be been busy with DropCue, which is not just another "to-do-list" or calendar management app, but an app to manage all of your "personal aspects" in one single place in such a simple, easy and innovative approach that no other system offers.
 
Give it a try, I bet you'll love it!
Follow on   Twitter

Comments and Discussions

 
QuestionOnCheckedChanged event for Checkbox Pinmemberstapes28-Mar-14 0:25 
QuestionIgnore Changes to Element Pinmemberdbiles13-Mar-14 9:17 
Questionhow to do some operation on select of leave this page by user... PinmemberAgustus Jackson24-Feb-14 23:37 
QuestionIs it possible to add this feature to a DropDownList with AutoPostBack set to true? Pinmemberstapes18-Nov-13 5:42 
AnswerRe: Is it possible to add this feature to a DropDownList with AutoPostBack set to true? Pinmemberstapes19-Nov-13 7:42 
AnswerRe: Is it possible to add this feature to a DropDownList with AutoPostBack set to true? PinmemberDarthJay13-Dec-13 8:14 
GeneralMy vote of 5 PinmemberJay N.11-Sep-13 5:42 
Question"Navigate Away" feature doesn't work Pinmemberankr0229-Nov-12 5:13 
QuestionNot working with jquery 1.7.2 Pinmemberfaizan921119-Jul-12 21:27 
AnswerRe: Not working with jquery 1.7.2 Pinmemberfaizan921119-Jul-12 21:34 
QuestionImplementing your code in a sub-folder PinmemberElikhater2-Jun-12 19:46 
AnswerRe: Implementing your code in a sub-folder PinmemberElikhater2-Jun-12 20:17 
GeneralMy vote of 5 Pinmembermanoj kumar choubey2-Apr-12 1:14 
Generalmy vote of 5 PinmemberUday P.Singh30-Dec-11 6:59 
QuestionSupport Gridview also bypass select button/pager in side gridview PinmemberCasp28-Jun-11 9:31 
GeneraljQuery 1.5.1 issue with elements that have a . in their name Pinmemberajbeaven4-May-11 18:28 
GeneralRe: jQuery 1.5.1 issue with elements that have a . in their name Pinmemberfaizan921119-Jul-12 21:33 
QuestionUsing in conjunction with jquery ui dialogs Pinmemberajbeaven3-May-11 16:17 
Generalbeginner Pinmembersandtrap24-Feb-11 7:21 
QuestionHow to use nonavigate having button in update panels ? Pinmemberyerroju3-Feb-11 23:36 
GeneralMy vote of 5 Pinmemberprasad0222-Dec-10 4:13 
GeneralMy Vote of 5 PinmemberRaviRanjankr21-Dec-10 4:17 
GeneralLocalization PinmemberMember 135414215-Dec-10 8:01 
Generalgood idea PinmemberPranay Rana14-Dec-10 23:52 
good one
For any question : http://pranayamr.blogspot.com/
 
vote my article :

Learn SQL to LINQ ( Visual Representation )


Calling WCF Services using jQuery

GeneralMy vote of 2 PinmemberSmirkinGherkin14-Dec-10 1:56 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140709.1 | Last Updated 14 Dec 2010
Article Copyright 2010 by Al-Farooque Shubho
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid