简体   繁体   English

Chrome 中的 window.onbeforeunload ajax 请求

[英]window.onbeforeunload ajax request in Chrome

I have a web page that handles remote control of a machine through Ajax. When user navigate away from the page, I'd like to automatically disconnect from the machine.我有一个 web 页面,它通过 Ajax 处理机器的远程控制。当用户离开该页面时,我想自动断开与机器的连接。 So here is the code:所以这是代码:

window.onbeforeunload = function () {
  bas_disconnect_only();
}

The disconnection function simply send a HTTP GET request to a PHP server side script, which does the actual work of disconnecting:断开连接 function 只需将 HTTP GET 请求发送到 PHP 服务器端脚本,该脚本完成断开连接的实际工作:

function bas_disconnect_only () {
   var xhr = bas_send_request("req=10", function () {
   });
}

This works fine in FireFox. But with Chrome, the ajax request is not sent at all.这在 FireFox 中工作正常。但是对于 Chrome,根本不会发送 ajax 请求。 There is a unacceptable workaround: adding alert to the callback function:有一个不可接受的解决方法:向回调 function 添加警报:

function bas_disconnect_only () {
   var xhr = bas_send_request("req=10", function () {
     alert("You're been automatically disconnected.");
   });
}

After adding the alert call, the request would be sent successfully.添加警报调用后,请求将成功发送。 But as you can see, it's not really a work around at all.但正如您所看到的,这根本不是真正的解决方法。

Could somebody tell me if this is achievable with Chrome?有人可以告诉我这是否可以通过 Chrome 实现? What I'm doing looks completely legit to me.我正在做的事情在我看来完全合法。

Thanks,谢谢,

This is relevant for newer versions of Chrome.这与较新版本的 Chrome 相关。

Like @Garry English said , sending an async request during page onunload will not work, as the browser will kill the thread before sending the request.正如onunload English所说,在页面卸载期间发送async请求是行不通的,因为浏览器会在发送请求之前终止线程。 Sending a sync request should work though.发送sync请求应该可以。

This was right until version 29 of Chrome, but on Chrome V 30 it suddenly stopped working as stated here .直到 Chrome 版本 29 之前都是这样,但是在 Chrome V 30 上它突然停止工作,如此处所述

It appears that the only way of doing this today is by using the onbeforeunload event as suggested here .看来,今天执行此操作的唯一方法是使用此处建议onbeforeunload事件。

BUT NOTE: other browsers will not let you send Ajax requests in the onbeforeunload event at all.但请注意:其他浏览器根本不允许您在 onbeforeunload 事件中发送 Ajax 请求。 so what you will have to do is perform the action in both unload and beforeunload, and check whether it had already taken place.所以你需要做的是在卸载和卸载之前执行操作,并检查它是否已经发生。

Something like this:像这样:

var _wasPageCleanedUp = false;
function pageCleanup()
{
    if (!_wasPageCleanedUp)
    {
        $.ajax({
            type: 'GET',
            async: false,
            url: 'SomeUrl.com/PageCleanup?id=123',
            success: function ()
            {
                _wasPageCleanedUp = true;
            }
        });
    }
}


$(window).on('beforeunload', function ()
{
    //this will work only for Chrome
    pageCleanup();
});

$(window).on("unload", function ()
{
    //this will work for other browsers
    pageCleanup();
});

I was having the same problem, where Chrome was not sending the AJAX request to the server in the window.unload event.我遇到了同样的问题,Chrome 没有在 window.unload 事件中向服务器发送 AJAX 请求。

I was only able to get it to work if the request was synchronous.如果请求是同步的,我只能让它工作。 I was able to do this with Jquery and setting the async property to false :我能够使用 Jquery 并将async属性设置为false来做到这一点:

$(window).unload(function () {
   $.ajax({
     type: 'GET',
     async: false,
     url: 'SomeUrl.com?id=123'
   });
});

The above code is working for me in IE9, Chrome 19.0.1084.52 m, and Firefox 12.上面的代码适用于 IE9、Chrome 19.0.1084.52 m 和 Firefox 12。

Checkout the Navigator.sendBeacon() method that has been built for this purpose.检查为此目的构建的Navigator.sendBeacon()方法。

The MDN page says: MDN 页面说:

The navigator.sendBeacon() method can be used to asynchronously transfer small HTTP data from the User Agent to a web server. navigator.sendBeacon() 方法可用于将小型 HTTP 数据从用户代理异步传输到 Web 服务器。

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.此方法解决了分析和诊断代码的需求,这些代码通常会在卸载文档之前尝试将数据发送到 Web 服务器。 Sending the data any sooner may result in a missed opportunity to gather data.过早发送数据可能会导致错失收集数据的机会。 However, ensuring that the data has been sent during the unloading of a document is something that has traditionally been difficult for developers.然而,确保在卸载文档期间已发送数据对于开发人员来说历来是一件困难的事情。

This is a relatively newer API and doesn't seems to be supported by IE yet.这是一个相对较新的 API,IE 似乎还不支持它。

Synchronous XMLHttpRequest has been deprecated ( Synchronous and asynchronous requests ).同步XMLHttpRequest已被弃用( 同步和异步请求)。 Therefore, jQuery.ajax() 's async: false option has also been deprecated.因此, jQuery.ajax()async: false选项也已弃用。

It seems impossible (or very difficult) to use synchronous requests during beforeunload or unload ( Ajax Synchronous Request Failing in Chrome ).beforeunloadunload期间使用同步请求似乎不可能(或非常困难)( Ajax Synchronous Request Failing in Chrome )。 So it is recommended to use sendBeacon and I definitely agree!所以推荐使用sendBeacon ,我绝对同意!

Simply:简单地:

window.addEventListener('beforeunload', function (event) {  // or 'unload'
    navigator.sendBeacon(URL, JSON.stringify({...}));

    // more safely (optional...?)
    var until = new Date().getTime() + 1000;
    while (new Date().getTime() < until);
});

Try creating a variable (Boolean preferably) and making it change once you get a response from the Ajax call.尝试创建一个变量(最好是布尔值)并在您从 Ajax 调用获得响应后更改它。 And put the bas_disconnect_only() function inside a while loop.并将bas_disconnect_only()函数放在 while 循环中。 I also had a problem like this once.我也曾经遇到过这样的问题。 I think this happens because Chrome doesn't wait for the Ajax call.我认为发生这种情况是因为 Chrome 不等待 Ajax 调用。 I don't know how I fixed it and I haven't tried this code out so I don't know if it works.我不知道我是如何修复它的,我还没有尝试过这段代码,所以我不知道它是否有效。 Here is an example of this:这是一个例子:

var has_disconnected = false;
window.onbeforeunload = function () {
    while (!has_disconnected) {
        bas_disconnect_only();
        // This doesn't have to be here but it doesn't hurt to add it:
        return true;
    }
}

And inside the bas_send_request() function ( xmlhttp is the HTTP request):bas_send_request()函数内部( xmlhttp是 HTTP 请求):

xmlhttp.onreadystatechange = function() {
    if (xmlhttp.readyState == 4 && xmlhttp.status == 200)
        has_disconnected = true;
}

Good luck and I hope this helps.祝你好运,我希望这会有所帮助。

I had to track any cases when user leave page and send ajax request to backend.当用户离开页面并向后端发送 ajax 请求时,我必须跟踪所有情况。

var onLeavePage = function() {
    $.ajax({
        type: 'GET',
        async: false,
        data: {val1: 11, val2: 22},
        url: backend_url
    });
};

/**
 * Track user action: click url on page; close browser tab; click back/forward buttons in browser
 */
var is_mobile_or_tablet_device = some_function_to_detect();
var event_name_leave_page = (is_mobile_or_tablet_device) ? 'pagehide' : 'beforeunload';
window.addEventListener(event_name_leave_page, onLeavePage);

/**
 * Track user action when browser tab leave focus: click url on page with target="_blank"; user open new tab in browser; blur browser window etc.
 */
(/*@cc_on!@*/false) ?  // check for Internet Explorer
    document.onfocusout = onLeavePage :
    window.onblur = onLeavePage;

Be aware that event "pagehide" fire in desktop browser, but it doesn't fire when user click back/forward buttons in browser (test in latest current version of Mozilla Firefox).请注意,事件“pagehide”在桌面浏览器中触发,但当用户在浏览器中单击后退/前进按钮时不会触发(在最新版本的 Mozilla Firefox 中测试)。

Try navigator.sendBeacon(...);

try {
        // For Chrome, FF and Edge
        navigator.sendBeacon(url, JSON.stringify(data));
    }
    catch (error)
    {
        console.log(error);
    }

    //For IE
    var ua = window.navigator.userAgent;
    var isIEBrowser = /MSIE|Trident/.test(ua);

    if (isIEBrowser) {
        $.ajax({
            url: url,
            type: 'Post',
            .
            .
            .
        });
    }

I've been searching for a way in which leaving the page is detected with AJAX request.我一直在寻找一种通过AJAX请求检测离开页面的方法。 It worked like every time I use it, and check it with MySQL.它就像我每次使用它时一样工作,并用 MySQL 检查它。 This is the code (worked in Google Chrome ):这是代码(在Google Chrome中运行):

    $(window).on("beforeunload", function () {
        $.ajax({
             type: 'POST',
             url: 'Cierre_unload.php',
             success: function () {
             }
        })
    })

I felt like there wasn't an answer yet that summarized all the important information, so I'm gonna give it a shot:我觉得还没有一个总结所有重要信息的答案,所以我要试一试:

Using asynchronous AJAX requests is not an option because there is no guarantee that it will be sent successfully to the server.使用异步 AJAX 请求不是一种选择,因为不能保证它会成功发送到服务器。 Browsers will typically ignore asynchronous requests to the server.浏览器通常会忽略对服务器的异步请求。 It may, or may not, be sent.它可能会或可能不会被发送。 ( Source ) 来源

As @ghchoi has pointed out, synchronous XMLHTTPRequests during page dismissal have been disallowed by Chrome ( Deprecations and removals in Chrome 80 ).正如@ghchoi指出的那样,Chrome 不允许在关闭页面期间同步 XMLHTTPRequests( Chrome 80 中的 Deprecations and removals )。 Chrome suggests using sendBeacon() instead. Chrome 建议改用sendBeacon()

According to Mozilla's documentation though, it is not reliable to use sendBeacon for unload or beforeunload events.不过,根据 Mozilla 的文档,将 sendBeacon 用于卸载或 beforeunload 事件并不可靠。

In the past, many websites have used the unload or beforeunload events to send analytics at the end of a session.过去,许多网站使用 unload 或 beforeunload 事件在会话结束时发送分析。 However, this is extremely unreliable.然而,这是极不可靠的。 In many situations, especially on mobile, the browser will not fire the unload, beforeunload, or pagehide events.在许多情况下,尤其是在移动设备上,浏览器不会触发卸载、卸载前或页面隐藏事件。

Check the documentation for further details: Avoid unload and beforeunload查看文档以获取更多详细信息: Avoid unload and beforeunload

Conclusion: Although Mozilla advises against using sendBeacon for this use case, I still consider this to be the best option currently available.结论:虽然 Mozilla 建议不要在这个用例中使用 sendBeacon,但我仍然认为这是目前可用的最佳选择。

When I used sendBeacon for my requirements, I was struggling to access the data sent at the server side (PHP).当我使用 sendBeacon 满足我的要求时,我很难访问在服务器端 (PHP) 发送的数据。 I could solve this issue using FormData as recommended in this answer .我可以按照此答案中的建议使用FormData解决此问题。

For the sake of completeness, here's my solution to the question:为了完整起见,这是我对问题的解决方案:

window.addEventListener('beforeunload', function () {
    bas_disconnect_only();
});

function bas_disconnect_only () {
    const formData = new FormData();
    formData.append(name, value);
    navigator.sendBeacon('URL', formData);
}

To run code when a page is navigated away from, you should use the pagehide event over beforeunload.要在页面导航离开时运行代码,您应该在 beforeunload 上使用pagehide事件 See the beforeunload usage notes on MDN .请参阅MDN 上的 beforeunload 使用说明

On that event callback, you should use Navigator.sendBeacon() , as Sparky mentioned .在该事件回调中,您应该使用Navigator.sendBeacon() ,正如Sparky 提到的那样。

// use unload as backup polyfill for terminationEvent
const terminationEvent = "onpagehide" in self ? "pagehide" : "unload";

window.addEventListener(terminationEvent, (event) => {
    navigator.sendBeacon("https://example.com/endpoint");
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM