window.onbeforeunload AJAX-запрос в Chrome

У меня есть веб-страница, которая обрабатывает удаленное управление машиной через Ajax. Когда пользователь уходит со страницы, я хотел бы автоматически отключиться от машины. Итак, вот код:

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

Функция отключения просто отправляет HTTP-запрос GET скрипту на стороне сервера PHP, который выполняет фактическую работу по отключению:

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

Это отлично работает в FireFox. Но с Chrome запрос ajax вообще не отправляется. Существует недопустимый обходной путь: добавление оповещения в функцию обратного вызова:

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

После добавления оповещения запрос будет успешно отправлен. Но, как вы можете видеть, это совсем не обходной путь.

Может кто-нибудь сказать мне, если это возможно с Chrome? То, что я делаю, выглядит совершенно законным для меня.

Спасибо,

10 ответов

Решение

У меня возникла та же проблема, когда Chrome не отправлял запрос AJAX на сервер в событии window.unload.

Я смог заставить его работать, только если запрос был синхронным. Я смог сделать это с помощью Jquery и установить для свойства async значение false:

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

Приведенный выше код работает для меня в IE9, Chrome 19.0.1084.52 m и Firefox 12.

Я знаю, что этот вопрос задавался некоторое время назад, и то, что я собираюсь сказать, относится к более новым версиям Chrome, но я думаю, что это может быть полезно для многих разработчиков.

Как сказал @Garry English, отправка асинхронного запроса во время загрузки страницы не будет работать, так как браузер прервет поток перед отправкой запроса. отправка запроса синхронизации должна работать, хотя.

Это было верно до версии 29 Chrome, но на Chrome V 30 он внезапно перестал работать, как указано здесь.

Похоже, что единственный способ сделать это сегодня - использовать событие onbeforeunload, как предлагается здесь.

НО ПРИМЕЧАНИЕ: другие браузеры вообще не позволяют отправлять запросы Ajax в событии onbeforeunload. так что вам нужно будет выполнить действие как в разгрузке, так и перед выгрузкой, и проверить, было ли оно уже выполнено.

Что-то вроде этого:

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

Надеется, что это помогает никому.

Проверьте метод Navigator.sendBeacon(), созданный для этой цели.

Страница MDN гласит:

Метод navigator.sendBeacon() можно использовать для асинхронной передачи небольших HTTP-данных из пользовательского агента на веб-сервер.

Этот метод учитывает потребности аналитического и диагностического кода, который обычно пытается отправить данные на веб-сервер до выгрузки документа. Отправка данных раньше может привести к упущенной возможности сбора данных. Однако обеспечение того, чтобы данные были отправлены во время выгрузки документа, является чем-то, что традиционно было трудным для разработчиков.

Это относительно новый API и пока не поддерживается IE.

Синхронный XMLHttpRequestустарел ( синхронные и асинхронные запросы). Следовательно,jQuery.ajax()с async: false опция также устарела.

Кажется невозможным (или очень сложным) использовать синхронные запросы во время beforeunload или unload( Ошибка синхронного запроса Ajax в Chrome). Поэтому рекомендуется использовать sendBeacon и я однозначно согласен!

Просто:

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

Попробуйте создать переменную (предпочтительно Boolean) и изменить ее, как только вы получите ответ от вызова Ajax. И положить bas_disconnect_only() функция внутри цикла while. У меня тоже когда-то была такая проблема. Я думаю, что это происходит потому, что Chrome не ждет вызова Ajax. Я не знаю, как это исправить, и я не пробовал этот код, поэтому я не знаю, работает ли он. Вот пример этого:

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

И внутри bas_send_request() функция (xmlhttp - это HTTP-запрос):

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

Удачи, и я надеюсь, что это поможет.

Я чувствовал, что еще не было ответа, который обобщил бы всю важную информацию, поэтому я попробую:

Использование асинхронных запросов AJAX недопустимо, поскольку нет гарантии, что они будут успешно отправлены на сервер. Браузеры обычно игнорируют асинхронные запросы к серверу. Оно может быть отправлено, а может и нет. ( Источник )

Как указал user4227175 , синхронные запросы XMLHTTPRequests во время закрытия страницы были запрещены Chrome ( Устаревание и удаление в Chrome 80). Chrome предлагает вместо этого использовать sendBeacon() .

Однако, согласно документации Mozilla, использование sendBeacon для событий unload или beforeunload ненадежно.

В прошлом многие веб-сайты использовали события unload или beforeunload для отправки аналитики в конце сеанса. Однако это крайне ненадежно. Во многих ситуациях, особенно на мобильных устройствах, браузер не запускает события unload, beforeunload или pagehide.

Дополнительные сведения см. в документации: Избегайте выгрузки и перед выгрузкой.

Вывод: хотя Mozilla не рекомендует использовать sendBeacon для этого варианта использования, я все же считаю, что это лучший вариант, доступный в настоящее время.

Когда я использовал sendBeacon для своих требований, я изо всех сил пытался получить доступ к данным, отправленным на стороне сервера (PHP). Я мог бы решить эту проблему, используя , как рекомендовано в FormDataэтом ответе .

Для полноты, вот мое решение вопроса:

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

function bas_disconnect_only () {
    const formData = new FormData();
    formData.append(name, value);
    navigator.sendBeacon('URL', formData);
}
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',
            .
            .
            .
        });
    }

Мне приходилось отслеживать любые случаи, когда пользователь покидает страницу и отправляет 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;

Имейте в виду, что событие "pagehide" запускается в настольном браузере, но не срабатывает, когда пользователь нажимает кнопки "назад" / "вперед" в браузере (протестируйте в последней текущей версии Mozilla Firefox).

Я искал способ, которым выход из страницы обнаруживается с помощью запроса AJAX. Это работало как каждый раз, когда я использую это, и проверяю это с MySQL. Это код (работает в Google Chrome):

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

Чтобы запустить код при переходе со страницы, вы должны использовать pagehideсобытие завершено перед выгрузкой. См. примечания по использованию перед выгрузкой на MDN.

В этом обратном вызове события вы должны использовать Navigator.sendBeacon(), как сказал Спарки .

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

window.addEventListener(terminationEvent, (event) => {
    navigator.sendBeacon("https://example.com/endpoint");
});
Другие вопросы по тегам