I am currently trying to send a request when an user closes the page. I am using the onbeforeunload
event.
The event triggers when the tab is closed or the page is refreshed. My event looks as follows:
window.onbeforeunload = function () {
$.ajax({ //jQuery
type: "POST",
url: "offline.php",
data: {
logout: 'false'
}
});
};
offline.php (this is not the plete script):
...
unset($_SESSION["onpage"];
if ($_POST['logout'] == "false") {
sleep(3);
if (isset($_SESSION["onpage"]) || !empty($_SESSION["onpage"])) die();
}
...
When a user closes the page, script unsets a session that is set on the page of the chat. After three seconds, the script should check if the user has returned but checking the onpage
session. However, the problem es in when I hit the refresh button. On refresh, the page does not load because the three seconds have pleted. This is causing my whole system to be ruined.
I have tried adding ignore_user_abort(true);
but it did not solve my issue. Is there any other way of getting this to work?
Side Note: This is for a chat. When a user closes the page, it should notify the other users on the chat with a "The user has left" message. This should not be displayed on refresh. When a user es back to the page, it should notify the other users that the user has returned with a "The user has entered" message.
I am currently trying to send a request when an user closes the page. I am using the onbeforeunload
event.
The event triggers when the tab is closed or the page is refreshed. My event looks as follows:
window.onbeforeunload = function () {
$.ajax({ //jQuery
type: "POST",
url: "offline.php",
data: {
logout: 'false'
}
});
};
offline.php (this is not the plete script):
...
unset($_SESSION["onpage"];
if ($_POST['logout'] == "false") {
sleep(3);
if (isset($_SESSION["onpage"]) || !empty($_SESSION["onpage"])) die();
}
...
When a user closes the page, script unsets a session that is set on the page of the chat. After three seconds, the script should check if the user has returned but checking the onpage
session. However, the problem es in when I hit the refresh button. On refresh, the page does not load because the three seconds have pleted. This is causing my whole system to be ruined.
I have tried adding ignore_user_abort(true);
but it did not solve my issue. Is there any other way of getting this to work?
Side Note: This is for a chat. When a user closes the page, it should notify the other users on the chat with a "The user has left" message. This should not be displayed on refresh. When a user es back to the page, it should notify the other users that the user has returned with a "The user has entered" message.
Share Improve this question edited Apr 6, 2016 at 3:35 Shawn31313 asked Mar 30, 2016 at 2:25 Shawn31313Shawn31313 6,0724 gold badges41 silver badges85 bronze badges 6- See stackoverflow./questions/1617412/… – guest271314 Commented Mar 30, 2016 at 2:50
-
1
You cannot depend on AJAX requests inside an
onbeforeunload
. They may or may not plete. – user229044 ♦ Commented Apr 6, 2016 at 2:51 - 2 Is there any requirement to use Ajax and not something like socket.io? With socket.io you can detect a disconnect on the server side. (I was editing as answer, but was it deleted -- this is more of a ment anyway) – noderman Commented Apr 6, 2016 at 2:59
- For now AJAX it is. I am still learning and not sure exactly how sockets work. However, I am using server sent events but I doubt those have the same functionality. @noderman – Shawn31313 Commented Apr 6, 2016 at 3:15
- All right -- on a not so side note, socket.io is practically born for chats: socket.io/demos/chat -- worth digging. – noderman Commented Apr 6, 2016 at 3:39
4 Answers
Reset to default 7 +100The Problem
I'm afraid what you're attempting to do isn't really possible, as there are many times in which onbeforeunload
won't get called, due to browser implementation, user preference, your pet zebra knocking your puter off the table, etc.
From Mozilla's beforeunload
documentation:
Note also that various mobile browsers ignore the result of the event (that is, they do not ask the user for confirmation). Firefox has a hidden preference in about:config to do the same. In essence this means the user always confirms that the document may be unloaded.
https://developer.mozilla/en-US/docs/Web/Events/beforeunload
The Solution
That doesn't mean handling users going offline is impossible, however.
Most chat web applications instead employ a heartbeat (a regular ping-like request), and use the distance between last ping to determine user disconnects. As an additional note, I would remend allowing a wider window than three seconds to determine user disconnects, because there are many reasons a ping may not make it within a three second timeframe.
Implementation Strategy
You have a few options to implement a ping. Most monly, people will use a window.setTimeout
to invoke the ping, which will restart the window.setTimeout upon pletion or failure, usually doubling the delay on successive failures so you aren't barraging a potentially-overloaded service.
setTimeout to Ping:
var i = 1000;
(function ping() {
$.ajax({
type: "POST",
url: "ping.php"
}).done(function() {
i = 1000;
window.setTimeout(ping, i);
}).fail(function() {
i = i * 2;
if (i > 60000) {
// We don't wait to wait longer than a minute
i = 60000;
}
window.setTimeout(ping, i);
});
}());
What about window.setInterval instead? Won't my code be shorter?
Please don't do that. My above note about "barraging an overloaded service"? It'll be plenty worse if you do that.
You really shouldn't be counting on onbeforeunload
or beforeunload
since a browser shouldn't require itself to do something on your behalf right before a user closes your webpage. If it did, it would open up a whole avenue of attack for malicious code where you could never close a page (think going to a torrent website and having 2 pages pop up every time you click on any DOM element, only now every time a user tries to close the tab, a recursive loop starts and prevents them from ever leaving without killing the browser through task manager).
Heartbeats
Most chat clients use a heartbeat system where the client automatically pings the server every x seconds. This method works - but it's costly and inefficient. You're forced to open and close new connections on a constant basis just to tell the server that you are still engaged.
Web Sockets
The contemporary way of building something like a chat system is to use web sockets. These are persistent connections established by the server between your web browser and the server. On a traditional web page, the server always reacts to the what the client does. The client makes a request, the server responds; this process repeats itself over and over again. The server never tells the client what to do and then wait for the client response. With web sockets, instead of having this one way munication channel, we have a pipeline that allows for a continuously open connection with bidirectional munication.
Creating Web Sockets
This isn't a trivial task. There's a lot of boilerplate code out of the gate you would have to write to get some basic functionalities working. But the good news is that you don't have to do this. There are several 3rd party JavaScript SDKs / back-end services that encapsulate all of the ground level browser implementation logic for you and make available to you a simple, clear interface. Think of their relationship to web sockets as what jQuery was to plain JavaScript. They standardize everything for you so you don't have to worry about writing low level web socket implementation and syncing up cross browser patibility.
One of my favorite web socket services is Firebase. Coincidentally, the demo project they showcase on their website is a real-time chat application built using the web socket support their service provides. Firebase can be set up to be an indepedent database source if you want it to be that. Or it can simply be used as a medium of munications between your traditional SQL database (which municates with it through a RESTful API) and client devices (browsers, apps, etc...).
I would like to point out that, as others have said, doing an AJAX request on the page unload event is highly unreliable.
However, you may be interested in Navigator.sendBeacon()
Relevant excerpt:
This method addresses the needs of analytics and diagnostics code that typically attempt to send data to a web server prior to the unloading of the document.
https://developer.mozilla/en-US/docs/Web/API/Navigator/sendBeacon
It is experimental, but once fully supported, should do what you want.
AJAX can get cut off as the page leaves.
You need to use navigator.sendBeacon()
instead.
https://developer.mozilla/en-US/docs/Web/API/Navigator/sendBeacon
window.addEventListener('unload', function () {
navigator.sendBeacon("offline.php", {
logout: 'false'
});
}, false);
This is simple, reliable, and needs no server smarts like juggling heartbeats entails.
Of course, like anything good in webdev, IE pat is lacking...