39

I am using window.onbeforeunload to prevent the user from navigating away after changing values on a form. This is working fine, except it also shows the warning when the user submits the form (not desired).

How can I do this without showing the warning when the form submits?

Current code:

var formHasChanged = false;

$(document).on('change', 'form.confirm-navigation-form input, form.confirm-navigation-form select, form.confirm-navigation-form textarea', function (e) {
    formHasChanged = true;
});

$(document).ready(function () {
    window.onbeforeunload = function (e) {
        if (formHasChanged) {
            var message = "You have not saved your changes.", e = e || window.event;
            if (e) {
                e.returnValue = message;
            }
            return message;
        }
    }
});
mtmurdock
  • 12,756
  • 21
  • 65
  • 108

4 Answers4

41

Using the form's submit event to set a flag might work for you.

 var formHasChanged = false;
 var submitted = false;

$(document).on('change', 'form.confirm-navigation-form input, form.confirm-navigation-form select, form.confirm-navigation-form textarea', function (e) {
    formHasChanged = true;
});

$(document).ready(function () {
    window.onbeforeunload = function (e) {
        if (formHasChanged && !submitted) {
            var message = "You have not saved your changes.", e = e || window.event;
            if (e) {
                e.returnValue = message;
            }
            return message;
        }
    }
 $("form").submit(function() {
     submitted = true;
     });
});
Jay Tomten
  • 1,657
  • 1
  • 14
  • 23
  • I accepted this over @Derek's because I already had custom submit code and needed to check the two values separately. – mtmurdock Aug 01 '12 at 21:15
  • I was using 1 variable before that, but it sometime works sometime not. Your solution working fine with 2 variables. Thanks – Js Lim Apr 18 '19 at 06:01
18

you could use .on() to bind onbeforeunload and then use .off() to unbind it in form submission

$(document).ready(function () {
    // Warning
    $(window).on('beforeunload', function(){
        return "Any changes will be lost";
    });

    // Form Submit
    $(document).on("submit", "form", function(event){
        // disable warning
        $(window).off('beforeunload');
    });
}
Brent White
  • 411
  • 5
  • 4
11

You can handle the submit() event, which will occur only for your form submission.

Within that event, set your flag variable formHasChanged to false to allow the unload to proceed. Also, just a suggestion, but since the purpose of that flag variable will have changed, so you may want to rename it something like 'warnBeforeUnload'

$(document).submit(function(){
    warnBeforeUnload = false;
});
Derek Hunziker
  • 12,996
  • 4
  • 57
  • 105
3

I was looking for a better solution to this. What we want is simply exclude one or more triggers from creating our "Are you sure?" dialog box. So we shouldn't create more and more workarounds for more and more side effects. What if the form is submitted without a click event of the submit button? What if our click-handler removes the isDirty status but then the form-submit is otherwise blocked afterwards? Sure we can change the behaviour of our triggers, but the right place would be the logic handling the dialog. Binding to the form's submit event instead of binding to the submit button's click event is an advantage of the answers in this thread above some others i saw before, but this IMHO just fixes the wrong approach.

After some digging in the event object of the onbeforeunload event I found the .target.activeElement property, which holds the element, which triggered the event originally. So, yay, it is the button or link or whatever we clicked (or nothing at all, if the browser itself navigated away). Our "Are you sure?" dialog logic then reduces itself to the following two components:

  1. The isDirty handling of the form:

    $('form.pleaseSave').on('change', function() {
      $(this).addClass('isDirty');
    });
    
  2. The "Are you sure?" dialog logic:

    $(window).on('beforeunload', function(event) {
      // if form is dirty and trigger doesn't have a ignorePleaseSave class
      if ($('form.pleaseSave').hasClass('isDirty')
          && !$(event.target.activeElement).hasClass('ignorePleaseSave')) {
        return "Are you sure?"
      }
      // special hint: returning nothing doesn't summon a dialog box
    });
    

It's simply as that. No workarounds needed. Just give the triggers (submit and other action buttons) an ignorePleaseSave class and the form we want to get the dialog applied to a pleaseSave class. All other reasons for unloading the page then summons our "Are you sure?" dialog.

P.S. I am using jQuery here, but I think the .target.activeElement property is also available in plain JavaScript.

spackmat
  • 892
  • 9
  • 23
  • 1
    This appears to be browser specific. Works well in Chrome but the target in Firefox is always `body` – Abraham Chan Sep 16 '15 at 04:04
  • Just tested in Firefox 40, event.target.activeElement gives in fact the clocked element, as it does in Chrome. My solution works well in both browsers for me. – spackmat Sep 16 '15 at 08:23
  • I like this approach but I couldn't get it to work in IE. But ignoring the event completely and using document.activeElement did the trick for me (only tested in Chrome and IE so far) – Doogal Sep 13 '16 at 12:07