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


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

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

9 65

9 ответов:

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

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

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

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

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

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

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

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

Теперь трудная часть, хотя, имея ваш 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, которая технически является тем, что она затем пытается перемещаться назад.

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

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

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

причина, по которой у меня есть восклицательный знак после знака хэша, заключается в том, чтобы удовлетворить парадигму Google "hashbang" (подробнее о), что полезно, если вы хотите быть проиндексированы поисковыми системами. Ваша хэш-строка обычно будет парами имя / значение, например #!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.

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

очевидно, вам нужно будет воздержаться от использования истории.замените государство и историю.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();