35

I don't have an actual use case for this, but I'm curious, whether there is a way to react (callback), if a user clicks on "stay on this page" when window.onbeforeunload was triggered.

http://jsfiddle.net/rWHU9/

function warning(){
    if(true){
      console.log('leaving');
      return "You are leaving the page";
    }
}
window.onbeforeunload = warning;​
Robin Drexler
  • 4,307
  • 3
  • 25
  • 28
  • I won't say duplicate, but here is a similar question that can probably answer the question: http://stackoverflow.com/questions/276660/how-can-i-override-the-onbeforeunload-dialog-and-replace-it-with-my-own – sachleen Aug 06 '12 at 20:21
  • 1
    @Giona: I don't think there is any solution to this problem that doesn't involve `setTimeout`. – gen_Eric Jan 14 '15 at 18:46

5 Answers5

27

There is no callback for staying on the page, but there is one for leaving the page, window.unload.

Try setting a timeout in beforeunload, then clear it in unload. If you stay, the timeout will run, otherwise it'll be cleared.

var timeout;

function warning() {
    timeout = setTimeout(function() {
        alert('You stayed');
    }, 1000);
    return "You are leaving the page";
}

function noTimeout() {
    clearTimeout(timeout);
}

window.onbeforeunload = warning;
window.unload = noTimeout;​

DEMO: http://jsfiddle.net/aPwfz/1/

gen_Eric
  • 223,194
  • 41
  • 299
  • 337
  • This one seems to work and looks quite good, although it also seems to work with a timeout of 1 instead of 1000. Was the 1000 just for demo purposes? – Robin Drexler Aug 06 '12 at 20:40
  • 1
    @Robin: The timeout needs to be long enough that it doesn't fire before `unload` does. 1000 was just the first number I thought of. :-P – gen_Eric Aug 06 '12 at 20:41
  • I think a 0 is better than a magic 1000, all you need to do is let the current event chain finish. There's no way that code in a `setTimeout` call can run before a chain of events finishes. – Ruan Mendes Dec 20 '13 at 22:07
  • 3
    @JuanMendes: With a timeout of `0`, it would run *before* the `unload` event. – gen_Eric Dec 21 '13 at 19:51
  • May I ask what is the purpose of clearing the timeout on `unload`? Since the window is unloading page's content, the timeout will be cleared, right? – m.spyratos Sep 12 '17 at 14:52
  • @m.spyratos The timeout may trigger *before* the page is unloaded, so I'm just making sure it's cleared when we don't want it to run. – gen_Eric Sep 12 '17 at 21:21
  • However, [you should not use the `unload` event](https://developer.mozilla.org/en-US/docs/Web/API/Window/unload_event#usage_notes) – Elijah Mock Jan 09 '23 at 23:50
10

Short answer: No, there is no callback or any direct way to intercept the "stay on this page" event, yet.

However, you can cheat a little and create a construct like this:

function warning() {
    setTimeout(function() {
        setTimeout(function() {
            alert('user choosed to stay on this site: ' + location.href);
        }, 100);
    },1);
    return "You are leaving the page";
}
window.onbeforeunload = warning;​

Since the question is a modal dialog, the setTimeout callback will not fire until you confirmed that modal box. But as you correctly assume now, this would also trigger if you click "leave this site". So this is tricky. To solve that, you would need to increase the setTimeout to give the browser enough time to unload the page.

Why there are two setTimeout()'s nested ? Well, the timer will "run" as soon as the modal dialog pops up. So we don't want to run that timer until the modal dialog was closed. As soon as that happens, our inner setTimeout will launch and do something over time.

I think a realistiv value for most real-world scenarios is about 2.000ms for the inner setTimeout

jAndy
  • 231,737
  • 57
  • 305
  • 359
5

The only way to tell that the user clicked cancel is to trigger a setTimeout and see if it fires. There is no way to interrogate the return from the system's confirm-esque box.

http://jsfiddle.net/rWHU9/2/

jbabey
  • 45,965
  • 12
  • 71
  • 94
  • This is the reason why onbeforunload should not trigger a scripts unload. This event is for warnings only! – FloydThreepwood Aug 06 '12 at 20:22
  • @zneak it has to be an interval larger than the page takes to finish the `onbeforeunload` and `unload` event. it will still show up if you make it too short. – jbabey Aug 06 '12 at 20:22
  • as I mentioned in my answer, having only one `setTimeout` call is flawed. – jAndy Aug 06 '12 at 20:25
  • @jAndy i did not understand your explanation of the double `setTimeout`, but i have seen it before, can you try explaining it in more detail? – jbabey Aug 06 '12 at 20:27
  • 2
    @jbabey: well, more precisely, its actually browser dependent. But some browser will handle the sequence of commands in a way, that the timeout period for the `setTimeout` runs up while the modal dialog is shown, so the code for `setTimeout` gets executed immediately the modal dialog disappears. Infact, we want that the `setTimeout` code is inserted to the UI queue after the dialog was closed, to give the browser a time window to actually leave the page. – jAndy Aug 06 '12 at 20:29
  • @jAndy oh i see, by modal you mean the confirm dialog box. in chrome (the browser i was using) it blocks the timer while it waits for user input. good to know. – jbabey Aug 06 '12 at 21:27
1

Try triggering a setTimeout as the first code to call any other function (in say 5-10 seconds) BEFORE the return "question here". i.e.

window.onbeforeunload = confirmExit;
function confirmExit() {
    setTimeout("thisFunction;", 10000);
    return "do you realy want to close?";
}

And to cancel the onbeforeclose this worked for me: try an empty function:

window.onbeforeunload = function(){};

Thanks, good luck!

0
$(window).bind('beforeunload', function(){
     return '\n Your custom message';
 });

With ‘beforeunload‘ event attached, when you close the page, hits the back button or reload the page, a confirmation box will prompt to ask you whether you really want to go, choose ok to exit the page: cancel to stop the page from exit and stay on the same page.

vickisys
  • 2,008
  • 2
  • 20
  • 27