22

What I'm trying to achieve is to Warn the user of unsaved changes if he/she tries to close a page or navigate away from it without saving first.

I've managed to get the OnBeforeUnload() dialog to pop-up... but I don't want it to be displayed at all if the user hasn't modified any field values. For this, I'm using this hidden input field called is_modified that starts with a default value of false and flips to true when any field is edited.

I tried to bind the change event to this is_modified field to try and detect for value change... and only then activate OnBeforeUnload.

$( '#is_modified' ).change( function() {
    if( $( '#is_modified' ).val() == 'true' )
        window.onbeforeunload = function() { return "You have unsaved changes."; }
});

But from what I figure is that the change() event works only after these 3 steps - a field receives focus, a value is changed and the field looses focus. In case of the hidden input field, I'm not sure how this receiving and loosing focus part works! Hence, the onbeforeunload function is never being activated.

Can anyone suggest a way to maintain a trigger over is_modified?

Thanks.

Ajinkya
  • 22,324
  • 33
  • 110
  • 161
miCRoSCoPiC_eaRthLinG
  • 2,910
  • 4
  • 40
  • 56

7 Answers7

31

I had a similar requirement so came up with following jQuery script:

$(document).ready(function() {
    needToConfirm = false; 
    window.onbeforeunload = askConfirm;
});

function askConfirm() {
    if (needToConfirm) {
        // Put your custom message here 
        return "Your unsaved data will be lost."; 
    }
}

$("select,input,textarea").change(function() {
    needToConfirm = true;
});

The above code checks the needToConfirm variable, if its true then it will display warning message. Whenever input, select or textarea elements value is changed, needToConfirm variable is set to true.


PS: Firefox > 4 don't allow custom message for onbeforeunload.
Reference: https://bugzilla.mozilla.org/show_bug.cgi?id=588292


UPDATE: If you are a performance freak, you will love @KyleMit's suggestion. He wrote a jQuery extension only() which will be executed only once for any element.

$.fn.only = function (events, callback) {
    //The handler is executed at most once for all elements for all event types.
    var $this = $(this).on(events, myCallback);
    function myCallback(e) {
        $this.off(events, myCallback);
        callback.call(this, e);
    }
    return this
};    

$(":input").only('change', function() {
    needToConfirm = true;
});
Community
  • 1
  • 1
Ajinkya
  • 22,324
  • 33
  • 110
  • 161
  • It works like a charm. But there is a little problem: I can't get the message changed. I would like to write my own message like: "Are you crazy you want to go away?" but i couldn't get it to work... – marianobianchi Jul 12 '13 at 16:27
  • Thanks for your answer, but i found that the problem was another. I'm using firefox and there is a feature that don't let change this message. I try it in chrome and it worked, but it won't work in firefox... – marianobianchi Jul 14 '13 at 21:53
  • @marianobianchi Yes after FIrefox 4 custom message is not allowed for `onbeforeunload` https://bugzilla.mozilla.org/show_bug.cgi?id=588292. Will update my answer accordingly – Ajinkya Jul 15 '13 at 04:56
  • 1
    **Couple Suggestions**: You could simplify the change selector by using jQuery's [`":input"` selector](http://api.jquery.com/input-selector/). Also, you don't need to keep listening to and firing change events after the first event sets `needToConfirm` to true. `.change()` is just shorthand for `.on('change')`. Instead of `on`, you can use [`.one('change')`](http://api.jquery.com/one/) so the event doesn't need to keep firing for subsequent changes. – KyleMit Oct 27 '14 at 15:51
  • @KyleMit Yup. Good suggestions. Will update my answer – Ajinkya Oct 27 '14 at 16:11
  • 1
    While we're at it, if you're amenable to adding extension methods, I wrote a 5 line extension method called [**`.only()`**](http://stackoverflow.com/a/24589200/1366033) that will only fire a single time as soon as it's called on any element. Whereas `one` "is executed at most once **per element** *per event type*." It's a little more code, but it's handy, cleaner, and less work to process. If you have a form with a lot of different fields that will keep firing, it could be helpful. [Here's a demo in fiddle](http://jsfiddle.net/KyleMit/23H7J/22/) – KyleMit Oct 27 '14 at 23:08
7

We just use Window.onbeforeunload as our "changed" flag. Here's what we're doing, (using lowpro):

Event.addBehavior({
  "input[type=radio]:change,input[type=text]:change,input[type=checkbox]:change,select:change": function(ev) {
       window.onbeforeunload = confirmLeave;
  }
  ".button.submit-button:click": function(ev) {
       window.onbeforeunload = null;
  },
});


function confirmLeave(){
    return "Changes to this form have not been saved. If you leave, your changes will be lost."  
}
Ajinkya
  • 22,324
  • 33
  • 110
  • 161
Bryan Larsen
  • 9,468
  • 8
  • 56
  • 46
7

The following works well in jQuery:

var needToConfirm = false;

$("input,textarea").on("input", function() {
  needToConfirm = true;
});

$("select").change(function() {
  needToConfirm = true;
});

window.onbeforeunload = function(){        
  if(needToConfirm) {
    return "If you exit this page, your unsaved changes will be lost.";        
  }
}   

And if the user is submitting a form to save the changes, you might want to add this (change #mainForm to the ID of the form they're submitting):

$("#mainForm").submit(function() {
  needToConfirm = false;
});
rybo111
  • 12,240
  • 4
  • 61
  • 70
  • This code worked for me, I just change last part so I dont need to write everitime diferent Id of #mainForm ` // Form Submit to not show warnings jQuery(document).on("submit", "form", function (event) { // disable warning needToConfirm = false; });` – Darko Aug 03 '21 at 09:31
3
$(window).bind('beforeunload',function() {
 return "'Are you sure you want to leave the page. All data will be lost!";
});

$('#a_exit').live('click',function() {
    $(window).unbind('beforeunload');
});

Above works For me.

Niko Jojo
  • 1,204
  • 2
  • 14
  • 31
1

Try your logic in a different manner. Meaning, put the logic for checking the value of the input field in your onbeforeunload method.

window.onbeforeunload = function () { 
    if ($("#is_modified").val() == 'true') { 
        return "You have unsaved changes."; 
    } else { 
        return true; // I think true is the proper value here 
    } 
};
Ajinkya
  • 22,324
  • 33
  • 110
  • 161
Jordan S. Jones
  • 13,703
  • 5
  • 44
  • 49
  • 1
    "true" is not the proper value. In some browsers (At least Firefox, perhaps more) returning "null" will cancel the dialog. Howerver, in IE, I could not find a way to cancel the dialog from within the handler. The only thing that worked for me was to remove the handler entirely beforehand (ie, "window.beforeunload = null"), as in Bryan Larsen's answer. – Jason C Jun 19 '09 at 21:00
  • Fair enough. Sadly, I didn't thoroughly test my solution before I answered. :( – Jordan S. Jones Jun 20 '09 at 01:56
  • 5
    I know I'm a little late to the party, but I was just running into this. However in IE if the function returns `undefined` (or doesn't return anything), the dialog doesn't show (this also works in FF). I tried returning true/false/null and IE would just put "true"/"false"/"null" as the text in the dialog. No idea why. Hope this helps someone else. – Jonathan Kuhn Feb 23 '11 at 23:39
  • I confirm @JonathanKuhn comment on Safari 8.0.7 – Oscar Pérez Jul 28 '15 at 13:41
0

in IE9 you can use simple return statement (re) which will not display any dialogue box. happy coding..

0

why not have the onbeforeunload call a function that checks if the values have changed, and if so return the "unsaved changes" confirm?

Ajinkya
  • 22,324
  • 33
  • 110
  • 161
Jonathan Fingland
  • 56,385
  • 11
  • 85
  • 79