Перехват вызова кнопки "Назад" в моем приложении AJAX: я не хочу, чтобы он что-либо делал

У меня есть приложение AJAX. Пользователь нажимает кнопку, и экран страницы изменяется. Они нажимают кнопку назад, ожидая перехода в исходное состояние, но вместо этого переходят на предыдущую страницу в своем браузере.

Как я могу перехватить и повторно назначить событие кнопки "Назад"? Я заглядывал в библиотеки, такие как RSH (которые я не мог получить на работу...), и я слышал, что использование хэш-тега как-то помогает, Но я не могу понять этого.

9 ответов


Ах, кнопка Назад. Вы можете представить, что" назад " запускает событие JavaScript, которое вы можете просто отменить так:

document.onHistoryGo = function() { return false; }

не так. Такого события просто нет.

Если у вас действительно есть веб-приложение (в отличие от просто веб-сайта с некоторыми функциями ajaxy) разумно взять на себя кнопку "назад" (с фрагментами на URL, как вы упомянули). Gmail делает это. Я говорю о том, когда ваше веб-приложение в все на одной странице, все сдержанный.

техника проста-всякий раз, когда пользователь принимает меры, которые изменяют вещи, перенаправить на тот же URL, вы уже на, но с другим хэш-фрагментом. Е. Г.

window.location.hash = "#deleted_something";
...
window.location.hash = "#did_something_else";

Если общее состояние вашего веб-приложения хэшируется, это отличное место для использования хэша. Скажем, у вас есть список писем, возможно, вы объедините все их идентификаторы и прочитанные/непрочитанные статусы и возьмете хэш MD5, используя его в качестве идентификатора фрагмента.

этот вид redirect (только хэш) не пытается получить что-либо с сервера, но вставляет слот в список истории браузера. Итак, в приведенном выше примере пользователь нажимает "назад", и теперь они показывают #deleted_something в адресной строке. Они ударили снова, и они все еще на Вашей странице, но без хэша. Потом снова вернулись, и они ... --8-->на самом деле вернуться туда, откуда они пришли.

Теперь трудная часть, хотя, имея ваш JavaScript обнаружить, когда пользователь ударил обратно (таким образом, вы можете вернуть состояние). Все, что вы делаете, это смотреть расположение окна и увидеть, когда он меняется. С опросом. (Я знаю, фу, опрос. Ну, нет ничего лучше кросс-браузера прямо сейчас). Вы не сможете сказать, пошли ли они вперед или назад, поэтому вам придется проявить творческий подход к вашим хэш-идентификаторам. (Возможно, хэш, объединенный с порядковым номером...)

В этом суть кода. (Это также то, как работает плагин истории jQuery.)

var hash = window.location.hash;
setInterval(function(){
    if (window.location.hash != hash) {
        hash = window.location.hash;
        alert("User went back or forward to application state represented by " + hash);
    }
}, 100);

чтобы дать актуальный ответ на старый (но популярный) вопрос:

введен в HTML5 тег history.pushState() и history.replaceState() методы, которые позволяют добавлять и изменять записи истории, соответственно. Эти методы работают в сочетании с window.onpopstate событие.

используя history.pushState() изменяет реферер, который используется в заголовке HTTP для XMLHttpRequest объекты, созданные после изменения состояния. Реферером будет URL-адрес документа, окно которого this at время создания


используя jQuery, я сделал простое решение:

$(window).bind('hashchange', function() {
    top.location = '#main';
    // Eventually alert the user describing what happened
});

пока тестируется только в Google Chrome.

это решило проблему для моего веб-приложения, которое также очень основано на AJAX.

это, возможно, немного hack'ish-но я бы назвал это элегантным взломом ; -) всякий раз, когда вы пытаетесь перемещаться назад, он всплывает хэш-часть в URI, которая технически то, что он затем пытается перемещаться назад.

он перехватывает оба нажатия браузера кнопка и мышь. И вы не можете bruteforce его назад, нажав несколько раз в секунду, что является проблемой, которая возникнет в решениях, основанных на setTimeout или setInterval.


Я действительно ценю объяснение, данное в ответ darkporter, но я думаю, что его можно улучшить, используя "hashchange" событие. Как объяснил darkporter, вы хотите убедиться, что все ваши кнопки меняют окно.местоположение.хэш-значение.

  • один из способов-это использовать <button> элементы, а затем присоединить к ним события, которые затем установить window.location.hash = "#!somehashstring";.
  • другой способ сделать это-просто использовать ссылки для вашего пуговицы, типа <a href="#!somehashstring">Button 1</a>. Хэш автоматически обновляется при нажатии на эти ссылки.

причина, по которой у меня есть восклицательный знак после хэш-знака, заключается в том, чтобы удовлетворить парадигму Google "hashbang" (подробнее о), что полезно, если вы хотите быть проиндексированы поисковыми системами. Ваша hashstring обычно будет парами имя / значение, как #!color=blue&shape=triangle или список типа #!/blue/triangle -- все, что имеет смысл для вашего веб-приложения.

тогда вам нужно только добавить это бит кода, который будет срабатывать при изменении хэш-значения (включая когда кнопка Назад нажмите). Нет необходимости в цикле опроса.

window.addEventListener("hashchange", function(){
    console.log("Hash changed to", window.location.hash);
    // .... Do your thing here...
});

Я не тестировал ничего, кроме Chrome 36, но согласно caniuse.com, это должно быть доступно в IE8+, FF21+, Chrome21+ и большинстве других браузеров, кроме Opera Mini.


это очень легко toi отключить кнопка браузера назад, как код JQuery ниже:

// History API 
if( window.history && window.history.pushState ){

  history.pushState( "nohb", null, "" );
  $(window).on( "popstate", function(event){
    if( !event.originalEvent.state ){
      history.pushState( "nohb", null, "" );
      return;
    }
  });
}

вы можете увидеть его работу и другие примеры здесь dotnsf и здесь thecssninja

спасибо !


из-за соображений конфиденциальности невозможно отключить кнопку "Назад" или изучить историю пользователя, но можно создавать новые записи в этой истории без изменения страниц. Всякий раз, когда приложение AJAX изменяет состояние, обновите top.location новая фрагмент URI.

top.location = "#new-application-state";

это создаст новую запись в стеке истории браузера. Ряд библиотек AJAX уже обрабатывают все скучные детали, такие как Очень Простой История.


в моей ситуации я хотел предотвратить кнопку "назад" от отправки пользователя на последнюю страницу и вместо этого предпринять другое действие. С помощью hashchange событие я придумал решение, которое сработало для меня. Этот скрипт предполагает, что страница, на которой Вы ее используете, еще не использует хэш, как в моем случае:

var hashChangedCount = -2;
$(window).on("hashchange", function () {
    hashChangedCount++;
    if (top.location.hash == "#main" && hashChangedCount > 0) {
        console.log("Ah ah ah! You didn't say the magic word!");
    }
    top.location.hash = '#main';
});
top.location.hash = "#";

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


Я разработал способ переопределить нормальное поведение истории и создать distinct back и forward события кнопки, используя API истории HTML5 (он не будет работать в IE 9). Это очень хаки, но эффективно, если вы хотите перехватить события кнопки назад и вперед и обрабатывать их, как вы хотите. Это может быть полезно в ряде сценариев, например, если вы отображаете окно удаленного рабочего стола и необходимо воспроизвести назад и вперед кнопки на пульте дистанционного управления машина.

следующее переопределит поведение кнопки назад и вперед:

var myHistoryOverride = new HistoryButtonOverride(function()
{
    console.log("Back Button Pressed");
    return true;
},
function()
{
    console.log("Forward Button Pressed");
    return true;
});

если вы вернули false в любой из этих функций обратного вызова, вы позволите браузеру продолжить обычную операцию назад/вперед и оставить свою страницу.

вот полный сценарий требуется:

function HistoryButtonOverride(BackButtonPressed, ForwardButtonPressed)
{
    var Reset = function ()
    {
        if (history.state == null)
            return;
        if (history.state.customHistoryStage == 1)
            history.forward();
        else if (history.state.customHistoryStage == 3)
            history.back();
    }
    var BuildURLWithHash = function ()
    {
        // The URLs of our 3 history states must have hash strings in them so that back and forward events never cause a page reload.
        return location.origin + location.pathname + location.search + (location.hash && location.hash.length > 1 ? location.hash : "#");
    }
    if (history.state == null)
    {
        // This is the first page load. Inject new history states to help identify back/forward button presses.
        var initialHistoryLength = history.length;
        history.replaceState({ customHistoryStage: 1, initialHistoryLength: initialHistoryLength }, "", BuildURLWithHash());
        history.pushState({ customHistoryStage: 2, initialHistoryLength: initialHistoryLength }, "", BuildURLWithHash());
        history.pushState({ customHistoryStage: 3, initialHistoryLength: initialHistoryLength }, "", BuildURLWithHash());
        history.back();
    }
    else if (history.state.customHistoryStage == 1)
        history.forward();
    else if (history.state.customHistoryStage == 3)
        history.back();

    $(window).bind("popstate", function ()
    {
        // Called when history navigation occurs.
        if (history.state == null)
            return;
        if (history.state.customHistoryStage == 1)
        {
            if (typeof BackButtonPressed == "function" && BackButtonPressed())
            {
                Reset();
                return;
            }
            if (history.state.initialHistoryLength > 1)
                history.back(); // There is back-history to go to.
            else
                history.forward(); // No back-history to go to, so undo the back operation.
        }
        else if (history.state.customHistoryStage == 3)
        {
            if (typeof ForwardButtonPressed == "function" && ForwardButtonPressed())
            {
                Reset();
                return;
            }
            if (history.length > history.state.initialHistoryLength + 2)
                history.forward(); // There is forward-history to go to.
            else
                history.back(); // No forward-history to go to, so undo the forward operation.
        }
    });
};

это работает по простой концепции. Когда наша страница загружается, мы создаем 3 различных состояния истории (пронумерованные 1, 2 и 3) и перемещаемся по браузер в состояние номер 2. Поскольку состояние 2 находится посередине, следующее событие навигации по истории приведет нас в состояние 1 или 3, и из этого мы можем определить, в каком направлении нажал пользователь. И вот так, мы перехватили событие кнопки Назад или вперед. Затем мы обрабатываем его, как хотим, и возвращаемся в состояние № 2, чтобы захватить следующее событие навигации по истории.

очевидно, вам нужно будет воздержаться от использования истории.replaceState и истории.pushState методы при использовании сценария HistoryButtonOverride, иначе вы бы его сломали.


Я делаю что-то вроде этого. Я сохраняю массив с предыдущими состояниями приложения.

начато как:

var backstack = [];

затем я слушаю изменения в хэше местоположения, и когда он меняется, я делаю это:

if (backstack.length > 1 && backstack[backstack.length - 2] == newHash) {
    // Back button was probably pressed
    backstack.pop();
} else
    backstack.push(newHash);

таким образом у меня несколько простой способ отслеживания истории пользователей. Теперь, если я хочу реализовать кнопку "Назад" в приложении (а не в браузере-Боттоне), я просто сделаю это:

window.location.hash = backstack.pop();