Подтвердить что ты не робот

Перехват вызова на кнопку возврата в моем приложении AJAX

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

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

4b9b3361

Ответ 1

Ах, кнопка назад. Вы можете себе представить, что "назад" запускает событие 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 обнаруживает, когда пользователь ударил (так что вы можете вернуть состояние). Все, что вы делаете, это посмотреть расположение окна и посмотреть, когда оно изменится. С опросом. (Я знаю, yuck, опрос. Ну, теперь нет лучшего кросс-браузера). Вы не сможете сказать, пошли ли они вперед или назад, поэтому вам нужно будет проявить творческий подход к своим идентификаторам хеширования. (Возможно, хэш, связанный с порядковым номером...)

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

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

Ответ 2

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

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

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

Источник: Манипулирование историей браузера из сети разработчиков Mozilla.

Ответ 3

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

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

Пока что проверено только в Google Chrome.

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

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

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

Ответ 4

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

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

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

Ответ 5

Очень легко отключить кнопку браузера BACK, например, код 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

Спасибо!

Ответ 6

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

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

Это создаст новую запись в стеке истории браузера. Ряд библиотек AJAX уже обрабатывает все скучные детали, такие как Really Simple History.

Ответ 7

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

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 не срабатывает. В зависимости от вашей ситуации это может сработать и для вас.

Ответ 8

Я разработал способ переопределить поведение обычной истории и создать различные события кнопки 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 в любой из этих функций обратного вызова, вы должны разрешить браузеру выполнять обычную операцию назад/вперед и оставить свою страницу.

Здесь требуется полный script:

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

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

Ответ 9

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

представьте простое приложение с двумя видами:

  • вид по умолчанию с кнопкой извлечения
  • просмотр результатов

чтобы реализовать это, следуйте этому примеру кода:

function goToView (viewName) {
  // before you want to change the view, you have to change window.location.hash.
  // it allows you to go back to the previous view with the back button.
  // so use this function to change your view instead of directly do the job.
  // page parameter is your key to understand what view must be load after this.

  window.location.hash = page
}

function loadView (viewName) {
    // change dom here based on viewName, for example:

    switch (viewName) {
      case 'index':
        document.getElementById('result').style.display = 'none'
        document.getElementById('index').style.display = 'block'
        break
      case 'result':
        document.getElementById('index').style.display = 'none'
        document.getElementById('result').style.display = 'block'
        break
      default:
        document.write('404')
    }
}

window.addEventListener('hashchange', (event) => {
  // oh, the hash is changed! it means we should do our job.
  const viewName = window.location.hash.replace('#', '') || 'index'

  // load that view
  loadView(viewName)
})


// load requested view at start.
if (!window.location.hash) {
  // go to default view if there is no request.
  goToView('index')
} else {
  // load requested view.
  loadView(window.location.hash.replace('#', ''))
}

Ответ 10

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

Инициируется как:

var backstack = [];

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

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

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

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