If it's remotely possible, change the requirement, because it will lead to a less-than-ideal user experience.
On at least some browsers, you can do a synchronous ajax request from the onbeforeunload
handler to send information to your server that the user is leaving the page:
window.onbeforeunload = function() {
$.ajax({
url: "/path/to/page",
data: {/* ...stuff... */,
async: false // <== Option is being removed soon
});
};
The reason I say it's a bad user experience is that since the ajax call is synchronous, it holds them up, and fairly intrusively on some browsers (the whole thing locks up while waiting for the request to complete). This is also why it may well not be reliable cross-browser.
The jQuery team is removing the async
option from ajax
at some point. It's still there in the underlying XMLHttpRequest
object, so when that happens you can use that directly.
The fact the user left the site is already apparent from the web server logs, although you can't tell how long they spent on the final page. If that "how long did they spend on the final page" is really vital information, rather than holding them up when they leave (and relying on something that may not be entirely reliable cross-browser), you could use a background "ping" while they're still on the page. It would probably be best to do the pings further and further apart over time.
So for instance, when the page loads:
(function($) {
var SECOND = 1000;
var MINUTE = 60000;
var arrived = +new Date();
var pingTimes = {
0: 10 * SECOND, // Every 10 seconds in first minute
1: 30 * SECOND, // Every 30 seconds in second minute
2: 45 * SECOND, // Every 45 seconds in third minute
other: 60 * SECOND, // Every minute otherwise
long: 10 * MINUTE // Every 10 minutes if they've been here a long time
};
nextPing();
function ping() {
$.ajax({
url: "/path/to/ping/page",
method: "POST",
success: nextPing,
error: nextPing
});
}
function nextPing() {
var elapsed, pingTime;
// Get the # of full minutes they've been here
elapsed = Math.floor((new Date() - arrived) / MINUTE);
// If it's been a long time, use `pingTimes.long`.
// Otherwise, use the time from the table or the default.
pingTime = elapsed > 15 * MINUTE ? pingTimes.long : (pingTimes[elapsed] || pingTimes.other);
setTimeout(ping, pingTime);
}
})(jQuery);