我如何查看用户何时关闭我的页面(捕获window.close事件),然后……没什么关系(我需要发送AJAX请求) ,但如果我可以运行警报,则可以执行其余操作。)
#1 楼
有unload
和beforeunload
javascript事件,但是这些事件对于Ajax请求是不可靠的(不能保证在这些事件之一中发起的请求都会到达服务器)。因此,这样做是强烈建议您不要使用它,而应该寻找替代方法。
如果您确实需要此方法,请考虑使用“ ping”式的解决方案。每分钟发送一次请求,基本上告诉服务器“我还在这里”。然后,如果服务器超过两分钟没有收到这样的请求(您必须考虑延迟等),则认为客户端处于脱机状态。
另一种解决方案是可以使用
unload
或beforeunload
来执行Sjax请求(同步JavaScript和XML),但是完全不建议这样做。这样做基本上会冻结用户的浏览器,直到请求完成为止,他们不会喜欢(即使请求花费很少的时间)。#2 楼
要弄清楚,在2018年(直到2020年今天),Beacon API是解决此问题的方法(几乎在所有浏览器上)
Beacon保证请求可以在页面卸载之前完成,
它们可以完成运行,而无需阻止请求。
您可以在
onunload
事件中使用它,并确信它会已发送。$(window).on('unload', function() {
var URL = "https://example.com/foo";
var data = "bar";
navigator.sendBeacon(URL, data);
});
评论
这个答案改变了我的生活!非常感谢!
– Alfie Robles
19/12/17在21:20
MDN说,卸载和beforeunload不是与sendBeacon一起使用的正确事件。而是使用可视性更改,这样对developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon毫无用处
– godblessstrawberry
12月4日21:10
#3 楼
1)如果您正在寻找一种在所有浏览器中均可使用的方法,那么最安全的方法是将同步AJAX发送到服务器。这不是一个好方法,但是至少要确保没有向服务器发送太多数据,并且服务器速度很快。2)您还可以使用异步AJAX请求,并在服务器上使用ignore_user_abort函数(如果使用的是PHP)。但是ignore_user_abort很大程度上取决于服务器配置。请确保您对其进行了良好的测试。
3)对于现代浏览器,您不应发送AJAX请求。您应该使用新的navigator.sendBeacon方法将数据异步发送到服务器,而又不会阻止下一页的加载。由于您希望在用户移出页面之前将数据发送到服务器,因此可以在卸载事件处理程序中使用此方法。
$(window).on('unload', function() {
var fd = new FormData();
fd.append('ajax_data', 22);
navigator.sendBeacon('ajax.php', fd);
});
似乎也可以成为sendBeacon的polyfill。如果方法本机不可用,则诉诸于发送同步AJAX。
对移动设备的重要提示:请注意,不能保证为移动设备触发卸载事件处理程序。但是可以保证能见度改变事件被触发。因此对于移动设备,您的数据收集代码可能需要一些调整。
您可以参考我的博客文章,了解这三种方式的代码实现。
评论
在2018年,这是最好的回应。使用信标API。阅读答案中提到的博客文章(有用的角度.com / post / 62 /…),以获取更多详细信息。
–白树
18-2-24在8:20
#4 楼
我还想实现相同的功能并从Felix那里得到这个答案(不能保证在这些事件之一中发起的请求将到达服务器)。使请求到达我们在以下代码中尝试过的服务器:-
onbeforeunload = function() {
//Your code goes here.
return "";
}
我们正在使用IE浏览器,现在当用户关闭浏览器时,由于
return "";
,他将收到确认对话框并等待用户确认&等待时间使请求到达服务器。评论
如果在beforeunload函数中不仅有retrun字符串语句,当前的Chrome似乎完全忽略了这一点。因此,您可以返回“您要关闭吗?”,但是您不能做其他任何事情……
–米哈尔(Michal Politzer)
17年3月30日在8:01
#5 楼
发表问题多年后,我提出了一种更好的实现方法,包括nodejs和socket.io(https://socket.io)(您可以使用任何类型的套接字,但这是我个人的选择)。基本上,我打开与客户端的连接,当它挂断时,我只是保存数据/做我需要的任何事情。显然,这不能用于显示任何内容/重定向客户端(因为您正在服务器端进行操作),但是那是我当时实际需要的。
io.on('connection', function(socket){
socket.on('disconnect', function(){
// Do stuff here
});
});
所以。 ..如今,我认为与卸载版本相比,这将是一种更好的方法(尽管由于您需要节点,套接字等而难以实现,但并不难;如果您是第一次使用,则大约需要30分钟) 。
#6 楼
所选答案是正确的,您不能保证浏览器会发送xhr请求,但是根据浏览器的不同,您可以可靠地在选项卡或窗口关闭时发送请求。通常,浏览器会关闭在xhr.send()实际执行之前。 Chrome和Edge看起来像它们在关闭窗口之前等待javascript事件循环清空。它们还会在与javascript事件循环不同的线程中触发xhr请求。这意味着,如果您可以将事件循环保持足够长的时间,则xhr将成功触发。例如,我测试了发送xhr请求,然后计数为100,000,000。这对我来说在Chrome和Edge上都非常稳定。如果您使用的是angularjs,则将对$ http的调用包装到$ apply中会完成相同的操作。
IE似乎有些不同。我不认为IE等待事件循环为空,甚至不等待当前的堆栈框架为空。尽管它偶尔会正确发送请求,但似乎更经常发生(80%-90%的时间)的是,IE会在xhr请求完全执行之前关闭窗口或选项卡,这只会导致部分消息正在发送。基本上,服务器接收到一个发布请求,但是没有任何内容。
为了后代,这是我用作窗口的代码。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++) {}
我在以下环境中进行过测试:
Chrome 61.0.3163.100
IE 11.608.15063.0CO
Edge 40.15063.0.0
评论
for循环所花费的时间将根据CPU,JS引擎和其他因素而有所不同。稍差一点的想法是将事件循环阻塞100ms或进行实验后认为足够长的任何其他值
–格雷格
17年11月1日在1:25
#7 楼
试试这个。我用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
});
}
评论
您好,欢迎来到。不降低投票权(因为您是新手:)),但是您的答案对接受的答案没有什么新的影响。另外,如果我正确阅读它,则每当用户将鼠标移到窗口外时,它将触发ConfirmLeave函数,从而导致虚假请求(例如,每次将鼠标移到任务栏上或其他位置时)。此外,它不处理任何触摸/移动情况(因为事件仅在mouseOut上绑定,在事件上移除,并且从不绑定)。还要考虑简单地按f5的情况(在您的示例中也不起作用)。
– zozo
19-09-5在17:48
在2011年(当我发布问题时),这是不可能的(或者至少不像今天这样简单),但是如今,您可以为此使用websocket,这基本上为您带来了ping样式解决方案的优点而没有缺点。如果您想查看有关该内容的信息,我会很乐意赞成这样的答案(我正在考虑自己张贴一个,但是...变得懒惰了:))。
– zozo
19年9月5日于17:50
#8 楼
我同意Felix的想法,并且已经使用该解决方案解决了我的问题,现在我想清除服务器端解决方案:1.将请求从客户端发送到服务器
2。保存上一个变量中收到的最后一个请求的时间
3.检查服务器时间并与上一个收到的变量中的变量进行比较
4 。如果结果超出预期的时间,请开始运行要在Windows关闭时运行的
代码...
#9 楼
使用:<body onUnload="javascript:">
它应该捕获除关闭浏览器程序以外的所有内容。
评论
嗯(-1),我很想知道这种方法有什么问题。我自己从未使用过它,但是已经多次遇到它作为您确切问题的解决方案。
–乔纳森
2011年5月28日14:30
我已经发布了一个答案,解释了-1。
–费利克斯
2011年5月28日下午14:32
评论
好吧,如果他正在注销,那么我已经看到了一些工作示例,其中卸载打开了一个弹出窗口,然后将用户注销(这解决了非担保人问题吗?)。
–乔纳森
2011年5月28日下午14:34
不,我敢打赌那里有弹出窗口阻止程序,这些事件不允许弹出窗口。同样,当用户关闭标签页/窗口时,打开一个弹出窗口也很糟糕。
–费利克斯
2011年5月28日下午14:36
我认为问题在于请求需要时间,并且浏览器可能会在完成时完成卸载(因此可能永远无法完成)?
–乔纳森
2011年5月28日下午14:36
我实际上正在做我仍然在这里的事情。但是有点慢并且减少用户体验。同样,减少“我仍然在这里”的时间间隔可能会以很高的用户率杀死服务器。所以我试图用其他东西代替它。当您说请求不可靠时,您的意思不可靠?我可以使用它,并以更高的间隔发送消息将其加倍,成功率超过50%。我认为这可以提高双方(服务器和客户端)的性能。
– zozo
2011年5月28日下午14:38
我认为这将是您最好的选择(尽管如果您想100%确定,则仍然必须执行ping操作)。顺便说一句,执行此操作的方法是返回“字符串”;在您的beforeunload处理程序中(执行Confirm()无效)。
–费利克斯
2011年5月29日19:58