1

I am trying to do an API call when the user is trying to close/reload the browser/tab. I don't want to call the API if the user clicks on cancel. I followed JavaScript, browsers, window close - send an AJAX request or run a script on window closing, but it didn't solve my issue. I followed catching beforeunload confirmation canceled? for differentiating between confirm and cancel. I have no idea how to make the API call when the user reloads/closes the browser and not to call the API when user clicks on cancel. I followed JavaScript, browsers, window close - send an AJAX request or run a script on window closing and tried like

For showing alert on reload or close the tab

<script>
    window.addEventListener("onbeforeunload", function(evt){
           evt.preventDefault()
           const string = '';
           evt.returnValue = string;
           return string;
   }) 
</script>

and on click of cancel, nothing should happen. If the user is forcefully closing the browser or reloading, the API should be called

<script type="module">
   import lifecycle from 'https://cdn.rawgit.com/GoogleChromeLabs/page-lifecycle/0.1.1/dist/lifecycle.mjs';
   lifecycle.addEventListener('statechange', function(event) {
       if (event.originalEvent === 'visibilitychange' && event.newState === 'hidden') {
           var URL = "https://api.com/" //url;
           var data = '' //payload;
           navigator.sendBeacon(URL, data);
       }
     });
</script>

But it's not happening. Any help is appreciated. Thanks

  • *I read catching beforeunload confirmation canceled?*, not [well enough it seems](https://stackoverflow.com/a/5259246/542251) – Liam Mar 05 '22 at 21:28
  • Please check https://stackoverflow.com/a/73062712/8798220, works like charm – Nisharg Shah Jul 21 '22 at 08:07

1 Answers1

2

Your problem is happening because you're using beforeunload to present a prompt.

I can see that you're handling the beforeunload event properly, so you must already be aware that browser vendors have deliberately limited the ability of script authors to do custom stuff when the user wants to leave the page. This is to prevent abuse.

Part of that limitation is that you don't get to find out what the user decides to do. And there will not be any clever workarounds, either. Once you tell the browser to present the beforeunload prompt, you lose all your power. If the user clicks the Okay button (i.e. decides to leave the page), the browser will refuse to run any more of your code.

Presenting the prompt creates a fork in the road that you are prevented from observing. So, put a laser tripwire there instead of a fork:

window.addEventListener("onbeforeunload", function(evt) {
  navigator.sendBeacon(url, payload)
})

This is guaranteed to run when the user actually leaves the page, and only when the user actually leaves the page. But, you sacrifice the ability to try to talk the user out of leaving. You can't have it both ways.

You can't always get what you want, but if you try, sometimes you just might find you get what you need. -- The Rolling Stones


I can only think of one way to accomplish what you need, but it requires help from the server. This is not an option for most people (usually because the beacon goes to a third-party analytics provider who won't do this), but I'm including it here for completeness.

  1. before the beforeunload handler returns, fire a beacon message that says "user is maybe leaving the page"
  2. after firing that beacon, and still before returning, set up a document-wide mousemove handler that fires a second beacon message that says "the user is still here" (and also de-registers itself)
  3. return false to present the prompt
  4. modify your server so that it will reconcile these two events after some kind of delay:
  • if the server receives beacon 1 and then also receives beacon 2 (within some reasonably short time-frame, e.g. 5 minutes), it means the user tried to leave but then changed their mind, and so the server should delete the record of beacon 1

  • if the server receives beacon 1 but doesn't receive beacon 2 within the time-frame, then it means the user really did leave, and so the server would rewrite the previous beacon datapoint to say "user actually departed"; you wouldn't need to actually write beacon 2 to your datastore

(Or, depending on expected traffic and your infrastructure, maybe the server just holds the beacon 1 datapoint in RAM for the 5 minutes and commits it to your datastore only if beacon 2 never shows up. Or you could write both beacons to the database and then have a different process reconcile the beacons later. The outcome is identical, but they have different performance characteristics and resource requirements.)


P.S.: Never use "URL" (all caps) as a variable name in javascript. "URL" is actually a useful web API, so if you use that exact variable name, you're clobbering a useful ability. It's just like if you did let navigator = 'Henry'. Yes, it will execute without error, but it shadows a useful native capability.

Tom
  • 8,509
  • 7
  • 49
  • 78