JavaScript、浏览器、窗口关闭 - 在窗口关闭时发送 AJAX 请求或运行脚本
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6162188/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
JavaScript, browsers, window close - send an AJAX request or run a script on window closing
提问by zozo
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:
我试图找出用户何时离开指定页面。找出他何时使用页面内的链接导航离开没有问题,但我需要标记一些内容,例如他关闭窗口或键入另一个 URL 并按下 Enter 键。第二个不是那么重要,但第一个很重要。那么问题来了:
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).
我如何才能看到用户何时关闭了我的页面(捕获 window.close 事件),然后......并不重要(我需要发送一个 AJAX 请求,但如果我可以让它运行警报,我可以做剩下的)。
回答by Felix
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).
有unload
和beforeunload
javascript 事件,但这些对于 Ajax 请求是不可靠的(不能保证在这些事件之一中发起的请求会到达服务器)。
Therefore, doing this is highly notrecommended, 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.
如果您确实需要这个,请考虑“ping”式解决方案。每分钟发送一个请求,基本上是告诉服务器“我还在这里”。然后,如果服务器超过两分钟没有收到这样的请求(你必须考虑延迟等),你认为客户端离线。
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).
另一种解决方案是使用unload
或beforeunload
执行 Sjax 请求(同步 JavaScript 和 XML),但完全不推荐这样做。这样做基本上会冻结用户的浏览器,直到请求完成,这是他们不会喜欢的(即使请求花费的时间很少)。
回答by Useful Angle
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.
1) 如果您正在寻找一种可以在所有浏览器中工作的方法,那么最安全的方法是向服务器发送同步 AJAX。这不是一个好方法,但至少要确保您没有向服务器发送太多数据,并且服务器速度很快。
2) You can also use an asynchronous AJAX request, and use ignore_user_abortfunction on the server (if you're using PHP). However ignore_user_abortdepends a lot on server configuration. Make sure you test it well.
2) 您也可以使用异步 AJAX 请求,并在服务器上使用ignore_user_abort函数(如果您使用的是 PHP)。然而ignore_user_abort 在很大程度上取决于服务器配置。确保你很好地测试它。
3) For modern browsers you should not send an AJAX request. You should use the new navigator.sendBeaconmethod 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 unloadevent handler.
3) 对于现代浏览器,您不应发送 AJAX 请求。您应该使用新的navigator.sendBeacon方法将数据异步发送到服务器,并且不会阻止下一页的加载。由于您希望在用户移出页面之前将数据发送到服务器,因此您可以在卸载事件处理程序中使用此方法。
$(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.
sendBeacon似乎也有一个polyfill。如果方法本身不可用,它会求助于发送同步 AJAX。
IMPORTANT FOR MOBILE DEVICES : Please note that unloadevent handler is not guaranteed to be fired for mobiles. But the visibilitychangeevent is guaranteed to be fired. So for mobile devices, your data collection code may need a bit of tweaking.
对移动设备的重要提示:请注意,不能保证为移动设备触发卸载事件处理程序。但是visibilitychange事件肯定会被触发。因此,对于移动设备,您的数据收集代码可能需要进行一些调整。
You may refer to my blog articlefor the code implementation of all the 3 ways.
3种方式的代码实现可以参考我的博客文章。
回答by Baishu
Basing this on other answer.
基于其他答案。
To make things clear, in 2018, Beacon API is the solution to this issue (on almost every browser)
澄清一下,在 2018 年,Beacon API 是这个问题的解决方案(几乎在每个浏览器上)
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.
您可以在onunload
event 中使用它,并确信它会被发送。
$(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
很棒的博客文章: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
信标 API : https://developer.mozilla.org/en-US/docs/Web/API/Beacon_API/Using_the_Beacon_API
回答by Pradeep Yadav
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).
我也想实现相同的功能并从 Felix那里找到了这个答案(不能保证在这些事件之一中发起的请求会到达服务器)。
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.
我们正在使用 IE 浏览器 & 现在当用户关闭浏览器时,他会收到确认对话框,因为return "";
& 等待用户的确认 & 这个等待时间使请求到达服务器。
回答by zozo
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).
发布问题多年后,我做了一个更好的实现,包括 nodejs 和 socket.io ( https://socket.io)(你可以使用任何类型的套接字,但这是我个人的选择)。
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.
所以......现在我认为这会是一种比卸载版本。
回答by codepuzzler
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.
选择的答案是正确的,您不能保证浏览器发送 xhr 请求,但取决于浏览器,您可以在选项卡或窗口关闭时可靠地发送请求。
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.
通常,浏览器在 xhr.send() 实际执行之前关闭。Chrome 和 edge 看起来像是在关闭窗口之前等待 javascript 事件循环清空。它们还在与 javascript 事件循环不同的线程中触发 xhr 请求。这意味着如果您可以将事件循环保持足够长的时间,那么 xhr 将成功触发。例如,我测试发送一个 xhr 请求,然后计数到 100,000,000。这对我来说在 chrome 和 edge 中都非常一致。如果您使用 angularjs,将您对 $http 的调用包装在 $apply 中可以完成同样的事情。
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.
IE似乎有点不同。我认为 IE 不会等待事件循环清空,甚至不会等待当前堆栈帧清空。虽然它偶尔会正确发送请求,但似乎更常见的情况(80%-90% 的时间)是 IE 将在 xhr 请求完全执行之前关闭窗口或选项卡,这只会导致部分消息被发送。基本上服务器收到一个发布请求,但没有正文。
For posterity, here's the code I used attached as the window.onbeforeunload listener function:
对于后人,这是我用作 window.onbeforeunload 侦听器函数的代码:
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
铬 61.0.3163.100
IE 11.608.15063.0CO
IE 11.608.15063.0CO
Edge 40.15063.0.0
边缘 40.15063.0.0
回答by Stidljivi
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).
试试这个。我在 javascript 中解决了这个问题,在浏览或选项卡关闭时向服务器发送 ajax 调用。我在刷新页面时遇到问题,因为 onbeforeunload 功能包括页面刷新。performance.navigation.type == 1 应该将刷新与关闭隔离(在 mozzila 浏览器上)。
$(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
});
}
回答by Ramin Farajpour
Im agree with Felix idea and I have solved my problem with that solution and now I wanna to clear the Server Side solution:
我同意 Felix 的想法,我已经用那个解决方案解决了我的问题,现在我想清除服务器端解决方案:
1.send a request from client side to server
1.从客户端向服务器发送请求
2.save time of the last request recived in a variable
2.保存最后一个请求的时间在一个变量中
3.check the server time and compare it by the variable of last recived request
3.检查服务器时间并与上次收到请求的变量进行比较
4.if the result is more than the time you expect,start running the code you want to run when windows closed...
4.如果结果超过你预期的时间,开始运行你想在windows关闭时运行的代码...
回答by Jonathon
Use:
用:
<body onUnload="javascript:">
It should capture everything except shutting down the browser program.
除了关闭浏览器程序之外,它应该捕获所有内容。