36

I am popping up a dialog box when someone tries to navigate away from a particular page without having saved their work. I use Javascript's onbeforeunload event, works great.

Now I want to run some Javascript code when the user clicks "Cancel" on the dialog that comes up (saying they don't want to navigate away from the page).

Is this possible? I'm using jQuery as well, so is there maybe an event like beforeunloadcancel I can bind to?

UPDATE: The idea is to actually save and direct users to a different webpage if they chose cancel

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
at.
  • 50,922
  • 104
  • 292
  • 461
  • I don't think it's possible... but I hope I'm wrong. – keithjgrant Jan 10 '11 at 19:45
  • I'm really curious how you block the navigator with a custom dialog. Could you provide the code ? – jAndy Jan 10 '11 at 19:46
  • @jAndy, look up the onbeforeunload event in Javascript, it does exactly this. – at. Jan 10 '11 at 19:55
  • @at: I know about the event, but this will only work the default modal dialog that pops up. Your post sounds like you're creating a custom UI dialog or something within the event handler. – jAndy Jan 10 '11 at 20:03

6 Answers6

62

You can do it like this:

$(function() {
    $(window).bind('beforeunload', function() {
        setTimeout(function() {
            setTimeout(function() {
                $(document.body).css('background-color', 'red');
            }, 1000);
        },1);
        return 'are you sure';
    });
}); 

The code within the first setTimeout method has a delay of 1ms. This is just to add the function into the UI queue. Since setTimeout runs asynchronously the Javascript interpreter will continue by directly calling the return statement, which in turn triggers the browsers modal dialog. This will block the UI queue and the code from the first setTimeout is not executed, until the modal is closed. If the user pressed cancel, it will trigger another setTimeout which fires in about one second. If the user confirmed with ok, the user will redirect and the second setTimeout is never fired.

example: http://www.jsfiddle.net/NdyGJ/2/

jAndy
  • 231,737
  • 57
  • 305
  • 359
  • I went to the link but nothing happened when I closed the window. Using Chrome... – Ruan Mendes Jan 10 '11 at 20:34
  • Very nice, so it works, not quite for my needs yet though. I don't understand the need for a nested setTimeout, removing the outer setTimeout seems to have the same effect. However, the problem is that the delayed function might get called when trying to "continue" and exit to the page you wanted. Delay it too long and hitting cancel doesn't seem to have an effect and delay it too short and you risk redirecting to the wrong page. – at. Jan 10 '11 at 22:01
  • In chrome you can set it to 0ms delay and it works. In IE you have to have a longer timeout or it freaks out. – Gordon Tucker Jan 14 '11 at 00:16
  • 1
    @jAndy. The SetTimeOut is also triggering while user select an Leave on this page option. How can we stop that. – VIJAY Mar 05 '12 at 06:50
  • @VIJAY: that's actually the reason for the nested `setTimeout`. The best thing we can do so far is to workaround the issue. In this example, it gives the browser/connection 1 second to leave the page, if that happens our code won't get executed. If it takes longer than 1 second, the code executes before pageunload. So, its not a very clean solution, but currently the best you can do. Of course, increasing that buffer-timeout is an option. – jAndy Jul 10 '12 at 15:42
  • 3
    As of Jan 2014, this approach doesn't work on chrome. – Scott Evernden Jan 10 '14 at 21:56
  • amazing answer, as a comment i just used one timeout and works in chrome, great, been searching for this for hours – Santiago Rebella May 26 '14 at 22:57
  • 1
    @ScottEvernden I just tested it with two recent versions of Chrome and it works (again?) Including version 40.0.2214.94 – timing Feb 02 '15 at 14:26
  • What if I want it the other way around? For Example: Call a logout method when the user clicked on okay button. And do nothing when the user clicked on cancel button. – user2918640 Jun 29 '15 at 12:58
  • @user2918640 You can use `var logout = confirm("Sure?");`. If **Ok** logout will have value as **true** else **false** – Abhi Jul 24 '15 at 10:20
  • Works fine with Chrome 47, but doesn't work on Firefox 42. Seems FF immediately executes code inside setTimeout (which was frozen while displaying alert box) if time passed exceeds timeout. So if timeout was 5s, but user was looking at an alert box for 6s, code will be executed, even before 'unload' event (so we can't reset the timer) – accme Dec 17 '15 at 10:20
  • 2
    This does not work on a slow connection: the second timeout is called regardless confirmation choice and could be executed before the actual unload (on a throttled Chrome). – Sjeiti Apr 25 '17 at 19:01
  • 1
    Doesn't work in Chrome 64. It reeeeeeeally doesn't want you to execute any code. I'm saving onblur. If you click "reload," it saves because the blur is fired. If you just hit enter, it doesn't trigger the onblur and thus does not save. – vbullinger Mar 05 '18 at 19:00
  • 1
    Actual page unload time depends on the time it takes to begin getting the next page. If the user's PC is slow, or if the next page's server is slow to respond, then this approach will erroneously decide that the user cancelled leaving. It's extemely unreliable. – Vsevolod Golovanov Sep 19 '19 at 10:38
  • Working fine for me in Chrome 111. Brilliant! – mpelzsherman Apr 11 '23 at 21:31
7

I know this question is old now, but in case anyone is still having issues with this, I have found a solution that seems to work for me,

Basically the unload event is fired after the beforeunload event. We can use this to cancel a timeout created in the beforeunload event, modifying jAndy's answer:

$(function() {
    var beforeUnloadTimeout = 0 ;
    $(window).bind('beforeunload', function()  {
        console.log('beforeunload');
        beforeUnloadTimeout = setTimeout(function() {
            console.log('settimeout function');
            $(document.body).css('background-color', 'red');
        },500);
        return 'are you sure';
    });
    $(window).bind('unload', function() {
        console.log('unload');
        if(typeof beforeUnloadTimeout !=='undefined' && beforeUnloadTimeout != 0)
            clearTimeout(beforeUnloadTimeout);
    });
}); 

EDIT: jsfiddle here

  • Did that work for you? Because in my case I get the `unload` like way after the timer function executes... (I tested in Firefox 35) – Alexis Wilke Jan 19 '15 at 13:48
  • Yea this works for me, try increasing the time for your timeout function. – 99 Problems - Syntax ain't one Jan 20 '15 at 11:36
  • I have tested some more and discovered that if I put about 5 seconds (instead of 0.5) it works as expected. But I think that's too much in case you cancel the dialog... Too bad we do not get any better feedback on that one. – Alexis Wilke Jan 20 '15 at 12:06
  • This is an excellent idea. However, it is affected by latency. At worst case, the Web server is turned off, and the length of the beforeunload timeout has to be equal to the browser's timeout. – Trevor Karjanis Sep 17 '18 at 00:45
2

Not possible. Maybe someone will prove me wrong... What code do you want to run? Do you want to auto-save when they click cancel? That sounds counter-intuitive. If you don't already auto-save, I think it makes little sense to auto-save when they hit "Cancel". Maybe you could highlight the save button in your onbeforeunload handler so the user sees what they need to do before navigating away.

Ruan Mendes
  • 90,375
  • 31
  • 153
  • 217
  • The idea is to actually then save and direct them to a different webpage if they chose cancel. – at. Jan 10 '11 at 19:54
2

I didn't think it was possible, but just tried this idea and it works (although it is some what of a hack and may not work the same in all browsers):

window.onbeforeunload = function () {   
  $('body').mousemove(checkunload);
  return "Sure thing"; 
};

function checkunload() {   
  $('body').unbind("mousemove");
  //ADD CODE TO RUN IF CANCEL WAS CLICKED
}
Barlow Tucker
  • 6,229
  • 3
  • 36
  • 42
  • 2
    if you move your mouse after clicking ok to leave site, the "cancel code" is run anyway... – at. Jan 14 '11 at 09:26
  • I guess you could add a timeout before binding the mousemove event, that may fix it, although that makes it even more of a hack. – Barlow Tucker Jan 14 '11 at 19:02
  • 2
    It takes more than 1 second for the existing window to disappear and if you move your mouse then, it would look like a cancellation. Having a timer has the same drawback as in jAndy's answer which does not work right for me. – Alexis Wilke Jan 19 '15 at 13:50
0

Another variation The first setTimeout waits for the user to respond to the browser's Leave/Cancel popup. The second setTimeout waits 1 second, and then CancelSelected is only called if the user cancels. Otherwise the page is unloaded and the setTimeout is lost.

window.onbeforeunload  = function(e) {
    e.returnValue = "message to user";
    setTimeout(function () { setTimeout(CancelSelected, 1000); }, 100);
}

function CancelSelected() {
    alert("User selected stay/cancel");
}
GaryH
  • 166
  • 2
  • 10
-7
window.onbeforeunload  = function() {
    if (confirm('Do you want to navigate away from this page?')) {
        alert('Saving work...(OK clicked)')
    } else {
        alert('Saving work...(canceled clicked)')
        return false
    }
 }

with this code also if user clicks on 'Cancel' in IE8 the default navigation dialog will appear.

Anto
  • 4,265
  • 14
  • 63
  • 113
jyoti
  • 1
  • 3
    You cannot use `confirm()` inside a function attached to `onbeforeunload`. – Alexis Wilke Jan 19 '15 at 13:42
  • @jyoti: It seems like you meant to comment on [someone else's answer](http://stackoverflow.com/a/4670938/177710v). To do so, click the `add a comment` link below a concrete answer. Please remove this answer from the thread as right now it's a duplicate of a wrong answer that will otherwise be downvoted soon. – Oliver Mar 17 '15 at 20:26