views:

811

answers:

3

I am working on ASP.NET 3.5 website project and using jQuery to warn users if they modified a page and attempt to leave the page without saving, so doing something like this:

var warning = "Please save changes you made.";
var isDirty = false;

$(document).ready(function()
{

      $('input:text,input:checkbox,input:radio').bind('change', function()
      {

            isDirty = true;

            $('input[type=submit]').bind('click', function(e)
            {

                isDirty = false;
            });

            window.onbeforeunload = function()
            {

                if (isDirty)
                {

                    return warning;
                }
            }
        });
    });

It works fine, but when I make changes on a page and then select a different item in a dropdown on a page, which causes a post back, I get a warning, which I should not.Is it feasible to implement this feature in jQuery, i.e. give warning on really leaving page, but ignoring postbacks, and what changes to the script I need to make in that case?

Ok, I modified script slightly, created a hidden field to save dirty state between postbacks, so it looks like this:

 var warn_on_unload = "Leaving this page will cause any unsaved data to be lost.";
 var previouslyDirty = false;
 var isDirty = false;

 $(window).load(function()
 {

    previouslyDirty = $('#dirtyFlag').val() == '1';

 });

 $(window).unload(function()
 {

        $('#dirtyFlag').val(previouslyDirty ? '1' : '0');
 });

 $(document).ready(function()
 {

        $('input:checkbox,input:radio').one('change', function()
        {

            isDirty = true;

            previouslyDirty = true;

        });



        $('input:text, textarea').keydown(function()

        {

            isDirty = true;

            previouslyDirty = true;

        });

        $('form').submit(function(e)
        {

            isDirty = false;

            previouslyDirty = false;

        });

        $('select').bind('change', function(e)
        {

            isDirty = false;

            previouslyDirty = true;

        });

        window.onbeforeunload = function()
        {

            if (isDirty || previouslyDirty)
            {

                return warn_on_unload;

            }
        }

    });

Still behaves incorrectly after non-submitting postbacks, which it should allow them without a warning, but saving dirty state, so if I change dropdown, no problem on postback , but if I try to leave page after that, should get a warning. Also, need to take of care of submitting postbacks, i.e. saving buttons, obviously allowing them and clearing all dirty flags previously set.

+1  A: 

Have you tried catching the submit event of the form ? (not the click of the submit button, but the actual submit)

maybe this would solve it ..

$('form').submit(function(){isDirty = false;});

unless the normal links also submit the form ...

Gaby
Thanks for reply. No, I haven't tried it. Problem is we use Master Page, and this is a content page, so no form name
Victor
yes.. but there is a form tag right ? (a single one). the code above attaches to the form tag ..
Gaby
Actually, no, page contents are entirely within Content tags
Victor
Doesn't the Masterpage have a single Form tag ?
Gaby
Ah, you mean on a Master page, yeah, I will give it a try.
Victor
@victor good luck :)
Gaby
thanks, looks like I need that while figuring this out:-)
Victor
A: 

How about something like this?

$(window).unload(function(event)
{
  if (event.target.nodeName != "SELECT" && event.target.nodeName != "INPUT" ) {
    $('#dirtyFlag').val(previouslyDirty ? '1' : '0');
  }
});
​

If this does not work, can you tell me what's the event.target.nodeName called under various circumstances?

Adhip Gupta
Thanks for reply. Value of event.target.nodeName is always '#document' since I have it on a master page
Victor
That's surprising. It's been a long time that I have worked with ASP.NET but I thought that the end result for the MasterPage was also HTML. Thus, if there is an event on SELECT (AutoPostback) the browser will always know the correct source of the event. The next thing I can suggest is that all postbacks are fired via a function called `__doPostBack`. You can maybe do something with that.
Adhip Gupta
+1  A: 

Since we already have jQuery, we use it to detect whether the form has changed as show in the accepted answer to this question http://stackoverflow.com/questions/598951/what-is-the-easiest-way-to-detect-if-at-least-one-field-has-been-changed-on-an-ht

Additionally, we know that webforms pages only have a single form. Webforms calls form.onsubmit() and form.submit() when handling a postback via a link.

To track the original serialized value across AutoPostBack events, store the serialized form data in a hidden field.

<asp:HiddenField id="serializedForm" runat="server" />

$(document).ready(function(){

    var theform = $('form');
    var serialized = theform.serialize();
    var previous = null;

    // track original serialized across postbacks
    var hidden = $('#<%# serializedForm.ClientID %>');
    if ('' == hidden.val()) {
        serialized = hidden.val();
    } else {
        hidden.val( serialized );
    }

    window.onbeforeunload = function(e) {
        if (serialized != theform.serialize()) {
            return "Are you sure you want to leave the page without saving?";
        }
    }

    // postback -> __doPostBack -> form.onsubmit -> here
    theform.submit(function(){
        // keep in case of validation fail
        previous = serialized;

        // prevent the onbeforeunload prompt
        serialized = theform.serialize();
    });

    // there might be ASP.NET validators
    if (typeof(ValidatorOnSubmit) == "function") {
        var asp_ValidatorOnSubmit = ValidatorOnSubmit;
        ValidatorOnSubmit = function() {
            var result = asp_ValidatorOnSubmit.apply( arguments );
            if (!result) {
                serialized = previous;
            }
            return result;
        }
    }

});
Lachlan Roche
Thanks for reply. I don't see how does this take care of non-submitting postbacks either, i.e. once I change something I will not be able to do anything that causes postback without a warning.
Victor
Yes, always a single form, it will be running on a master page. But I don't think can use this method as it is. After I click "Save" submit button following making some changes, I should be able to close a page without a warning, since I just saved my changes. My code above above takes care of that scenario.
Victor
@Victor I simplified my script a little
Lachlan Roche
@Lachlan. Yes, see my comment above
Victor
@Victor You are doing an ajax save?
Lachlan Roche
@Lachlan. Just a regular save.
Victor
@Thanks for the update. I know it's hard to take care of all scenarios, but looks like it will reset a dirty state after a postback, caused by any control, other then save button? Besides save button, I have many types of controls on a page, that cause a postback, hence my original post.
Victor
@Lachlan Please, see my previous comment
Victor
@Victor postbacks route via form.onsubmit, which will call jquery submit handler.
Lachlan Roche
@Lachlan Right, so this method will "clean" dirty state on every postback?
Victor
@Victor if we render a new page, the state is reset. if we performed an AJAX save we would need to reset var serialized
Lachlan Roche
@Lachlan Thanks for reply. Yes, I am doing a regular save, this is vanilla ASP.NET project. Looks like that state will be reset on every postback which I can't have, i.e. it should reset only when I click Save, not any other control on a page, that causes a postback.
Victor
Even though this is not exactly what I was asking for, I really appreciate the effort, so I will accept.
Victor