JavaScript,浏览器,窗口关闭 - 发送AJAX请求或在窗口关闭时运行脚本

时间:2011-05-28 14:19:34

标签: javascript browser window dom-events

我试图找出用户何时离开指定页面。找到他在页面内部使用链接导航时没有问题,但我需要标记一些内容,例如当他关闭窗口或输入另一个URL并按下回车时。第二个不是那么重要,但第一个是。所以这是一个问题:

如何查看用户何时关闭我的页面(捕获window.close事件),然后......并不重要(我需要发送一个AJAX请求,但是如果我可以让它运行警报,我可以做其余的事。)

9 个答案:

答案 0 :(得分:31)

unloadbeforeunload个javascript事件,但这些事件对于Ajax请求并不可靠(不能保证在其中一个事件中启动的请求会到达服务器)。

因此,这样做非常推荐,您应该寻找替代方案。

如果您确实需要这个,请考虑使用“ping”式解决方案。每分钟发送一个请求,基本上告诉服务器“我还在这里”。然后,如果服务器超过两分钟没有收到此类请求(您必须考虑延迟等),则认为客户端处于脱机状态。


另一种解决方案是使用unloadbeforeunload来执行Sjax请求(同步JavaScript和XML),但完全不建议这样做。这样做基本上会冻结用户的浏览器,直到请求完成,这是他们不喜欢的(即使请求花费的时间很少)。

答案 1 :(得分:17)

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);
});

似乎还有polyfill for sendBeacon。如果方法本身不可用,它会转向发送同步AJAX。

移动设备的重要事项:请注意,不保证会为手机触发卸载事件处理程序。但是保证 visibilitychange 事件被触发。因此,对于移动设备,您的数据收集代码可能需要进行一些调整。

您可以参考我的blog article获取所有3种方式的代码实现。

答案 2 :(得分:14)

基于other answer

为了说清楚,2018年,Beacon API是此问题的解决方案(在almost every browser上)

  

保证在卸载页面之前启动信标请求   它们运行完成,无需阻止请求。

您可以在onunload事件中使用它,并确信它会被发送。

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

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

    navigator.sendBeacon(URL, data);

});

很棒的博文: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

答案 3 :(得分:3)

我也希望实现相同的功能和来自Felix的回答( 无法保证在其中一个事件中启动的请求将到达服务器 )。

要使请求到达服务器,我们在下面尝试了代码: -

onbeforeunload = function() {

    //Your code goes here.

    return "";
} 

我们正在使用IE浏览器&现在,当用户关闭浏览器时,由于return "";&等待用户的确认&此等待时间使请求到达服务器。

答案 4 :(得分:1)

发布问题几年后,我做了一个更好的实现,包括nodejs和socket.io(https://socket.io)(您可以使用任何类型的套接字,但这是我个人的选择)。

基本上,我打开了与客户端的连接,当它挂断时,我只是保存数据/做我需要的任何事情。显然,这不能用于显示任何内容/重定向客户端(因为您正在服务器端进行操作),但这是我当时真正需要的。

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

所以...如今,我认为这会更好(尽管更难实现,因为您需要节点,套接字等,但并不那么困难;如果您第一次进行,大约需要30分钟)而不是卸载版本。

答案 5 :(得分:0)

我同意Felix的想法,我已经用该解决方案解决了我的问题,现在我想清除服务器端解决方案:

1.从客户端向服务器发送请求

2.在变量中收到的最后一次请求的保存时间

3.检查服务器时间并将其与上次收到的变量进行比较     请求

4.如果结果超出预期,请开始运行     Windows关闭时要运行的代码...

答案 6 :(得分:0)

所选答案是正确的,您无法保证浏览器发送xhr请求,但根据浏览器的不同,您可以在选项卡或窗口关闭时可靠地发送请求。

通常,浏览器在xhr.send()实际执行之前关闭。 Chrome和Edge看起来像是在关闭窗口之前等待javascript事件循环清空。他们还在与javascript事件循环不同的线程中触发xhr请求。这意味着如果您可以将事件循环保持足够长时间,则xhr将成功触发。例如,我测试了发送xhr请求,然后计数到100,000,000。对于我来说,这在chrome和edge中都非常一致。如果你正在使用angularjs,那么在$ apply中将你的调用包装成$ http就可以完成同样的事情。

IE似乎有点不同。我不认为IE等待事件循环为空,或者甚至是当前堆栈帧为空。虽然它偶尔会正确地发送请求,但似乎更常见的事情(80%-90%的时间)是IE将在xhr请求完全执行之前关闭窗口或选项卡,这导致只有部分消息被送了。基本上服务器收到一个帖子请求,但没有正文。

对于后人,这里我使用的代码作为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++) {}

我测试过:

Chrome 61.0.3163.100

IE 11.608.15063.0CO

Edge 40.15063.0.0

答案 7 :(得分:0)

  

尝试这个。我用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
   });
}

答案 8 :(得分:-3)

使用:

<body onUnload="javascript:">

除了关闭浏览器程序外,它应该捕获所有内容。