JavaScript, browsers, window close - send an AJAX request or run a script on window closing


I'm trying to find out when a user left a specified page. There is no problem finding out when he used a link inside the page to navigate away but I kind of need to mark up something like when he closed the window or typed another URL and pressed enter. The second one is not so important but the first one is. So here is the question:

How can I see when a user closed my page (capture window.close event), and then... doesn't really matter (I need to send an AJAX request, but if I can get it to run an alert, I can do the rest).

There are unload and beforeunload javascript events, but these are not reliable for an Ajax request (it is not guaranteed that a request initiated in one of these events will reach the server).

Therefore, doing this is highly not recommended, and you should look for an alternative.

If you definitely need this, consider a "ping"-style solution. Send a request every minute basically telling the server "I'm still here". Then, if the server doesn't receive such a request for more than two minutes (you have to take into account latencies etc.), you consider the client offline.


Another solution would be to use unload or beforeunload to do a Sjax request (Synchronous JavaScript And XML), but this is completely not recommended. Doing this will basically freeze the user's browser until the request is complete, which they will not like (even if the request takes little time).


1) If you're looking for a way to work in all browsers, then the safest way is to send a synchronous AJAX to the server. It is is not a good method, but at least make sure that you are not sending too much of data to the server, and the server is fast.

2) You can also use an asynchronous AJAX request, and use ignore_user_abort function on the server (if you're using PHP). However ignore_user_abort depends a lot on server configuration. Make sure you test it well.

3) For modern browsers you should not send an AJAX request. You should use the new navigator.sendBeacon method to send data to the server asynchronously, and without blocking the loading of the next page. Since you're wanting to send data to server before user moves out of the page, you can use this method in a unload event handler.

$(window).on('unload', function() {
    var fd = new FormData();
    fd.append('ajax_data', 22);
    navigator.sendBeacon('ajax.php', fd);
});

There also seems to be a polyfill for sendBeacon. It resorts to sending a synchronous AJAX if method is not natively available.

IMPORTANT FOR MOBILE DEVICES : Please note that unload event handler is not guaranteed to be fired for mobiles. But the visibilitychange event is guaranteed to be fired. So for mobile devices, your data collection code may need a bit of tweaking.

You may refer to my blog article for the code implementation of all the 3 ways.


Basing this on other answer.

To make things clear, in 2018, Beacon API is the solution to this issue (on almost every browser)

Beacon Requests are guaranteed to be initiated before a page is unloaded and they are run to completion, without requiring a blocking request.

You can use it inside onunload event, and be confident it will be sent.

$(window).on('unload', function() {

    var URL = "https://example.com/foo";
    var data = "bar";

    navigator.sendBeacon(URL, data);

});

Great blog post : http://usefulangle.com/post/62/javascript-send-data-to-server-on-page-exit-reload-redirect

Beacon API : https://developer.mozilla.org/en-US/docs/Web/API/Beacon_API/Using_the_Beacon_API


I also wanted to achieve the same functionality & came across this answer from Felix(it is not guaranteed that a request initiated in one of these events will reach the server).

To make the request reach to the server we tried below code:-

onbeforeunload = function() {

    //Your code goes here.

    return "";
} 

We are using IE browser & now when user closes the browser then he gets the confirmation dialogue because of return ""; & waits for user's confirmation & this waiting time makes the request to reach the server.


Years after posting the question I made a way better implementation including nodejs and socket.io (https://socket.io) (you can use any kind of socket for that matter but that was my personal choice).

Basically I open up a connection with the client, and when it hangs up I just save data / do whatever I need. Obviously this cannot be use to show anything / redirect the client (since you are doing it server side), but is what I actually needed back then.

 io.on('connection', function(socket){
      socket.on('disconnect', function(){
          // Do stuff here
      });
 });

So... nowadays I think this would be a better (although harder to implement because you need node, socket, etc., but is not that hard; should take like 30 min or so if you do it first time) approach than the unload version.


The selected answer is correct that you can't guarantee that the browser sends the xhr request, but depending on the browser, you can reliably send a request on tab or window close.

Normally, the browser closes before xhr.send() actually executes. Chrome and edge look like they wait for the javascript event loop to empty before closing the window. They also fire the xhr request in a different thread than the javascript event loop. This means that if you can keep the event loop full for long enough, the xhr will successfully fire. For example, I tested sending an xhr request, then counting to 100,000,000. This worked very consistently in both chrome and edge for me. If you're using angularjs, wrapping your call to $http in $apply accomplishes the same thing.

IE seems to be a little different. I don't think IE waits for the event loop to empty, or even for the current stack frame to empty. While it will occasionally correctly send a request, what seems to happen far more often (80%-90% of the time) is that IE will close the window or tab before the xhr request has completely executed, which result in only a partial message being sent. Basically the server receives a post request, but there's no body.

For posterity, here's the code I used attached as the window.onbeforeunload listener function:

  var xhr = new XMLHttpRequest();
  xhr.open("POST", <your url here>);
  xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
  var payload = {id: "123456789"};
  xhr.send(JSON.stringify(payload));

  for(var i = 0; i < 100000000; i++) {}

I tested in:

Chrome 61.0.3163.100

IE 11.608.15063.0CO

Edge 40.15063.0.0


Try this one. I solved this problem in javascript, sending ajax call to server on browse or tab closing. I had a problem with refreshing page because on onbeforeunload function including refreshing of the page. performance.navigation.type == 1 should isolate refresh from closing (on mozzila browser).

$(window).bind('mouseover', (function () { // detecting DOM elements
    window.onbeforeunload = null;
}));

$(window).bind('mouseout', (function () { //Detecting event out of DOM
      window.onbeforeunload = ConfirmLeave;
}));

function ConfirmLeave() {
   if (performance.navigation.type == 1) { //detecting refresh page(doesnt work on every browser)
   }
   else {
       logOutUser();
   }
}

$(document).bind('keydown', function (e) { //detecting alt+F4 closing
    if (e.altKey && e.keyCode == 115) {
        logOutUser();
    }    
});
function logOutUser() {
   $.ajax({
     type: "POST",
     url: GWA("LogIn/ForcedClosing"), //example controller/method
     async: false
   });
}

Im agree with Felix idea and I have solved my problem with that solution and now I wanna to clear the Server Side solution:

1.send a request from client side to server

2.save time of the last request recived in a variable

3.check the server time and compare it by the variable of last recived request

4.if the result is more than the time you expect,start running the code you want to run when windows closed...


Use:

<body onUnload="javascript:">

It should capture everything except shutting down the browser program.