Перехоплення дзвінка на кнопку "Назад" у моїй програмі AJAX


76

У мене є програма AJAX. Користувач натискає кнопку, і відображення сторінки змінюється. Вони натискають кнопку "Назад", очікуючи переходу в початковий стан, але замість цього переходять на попередню сторінку у своєму браузері.

Як я можу перехопити та повторно призначити подію кнопки повернення? Я переглядав такі бібліотеки, як RSH (і я не міг працювати ...), і чув, що використання тегу хешу якось допомагає, але я не можу цього зрозуміти.

Відповіді:


120

Ах, кнопка назад. Ви можете собі уявити, що "назад" запускає подію 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 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);

57

Щоб дати актуальну відповідь на старе (але популярне) запитання:

HTML5 представив методи history.pushState()і history.replaceState(), які дозволяють додавати та змінювати записи історії відповідно. Ці методи працюють разом із window.onpopstateподією.

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

Джерело: Маніпулювання історією браузера з мережі розробників Mozilla.


10

За допомогою jQuery я зробив просте рішення:

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

Поки що протестовано лише в Google Chrome.

Це вирішило проблему для мого веб-додатку, який також має високу базу даних AJAX.

Це, можливо, трохи хакерсько - але я б назвав це елегантним хакерством ;-) Щоразу, коли ви намагаєтесь орієнтуватися назад, він видає хеш-частину в URI, що технічно те, що він потім намагається перейти назад.

Він перехоплює як натискання кнопки браузера, так і кнопки миші. І ви не можете застосувати його назад, натиснувши кілька разів на секунду, що є проблемою, яка виникає у рішеннях на основі setTimeout або setInterval.


Я припускаю, що клацання правою кнопкою назад і повернення назад на два кроки все одно все одно спрацюють?
Hjulle

9

Я дуже ціную пояснення, дане у відповіді darkporter , але я думаю, що це можна покращити, використовуючи подію " hashchange " . Як пояснив darkporter, ви хочете переконатися, що всі ваші кнопки змінюють значення window.location.hash.

  • Один із способів - використовувати <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.


1
Тільки оновлення - Google більше не рекомендує підхід "hashbang", оскільки вони успішно скануються зараз webmasters.googleblog.com/2015/10/…
rmirabelle

7

Дуже легко вимкнути кнопку НАЗАД браузера , як код 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

Дякую !


1
Це був єдиний фрагмент на цій сторінці, який працював для мене в Chrome v52.0. Дякую.
Ralpharama,

це працює. але користувач може перейти на попередню сторінку, клацнувши правою кнопкою миші на кнопку назад.
Vimal

@VimalS ТАК! це все! вам потрібно налаштувати! будь ласка, ознайомтесь із асоційованими сторінками, які я надіслав! в історії API є набагато більше ... плюси: звичайний користувач знає "правильний клацання"? .. просто впав, я гадаю ... БУДЬ ЛАСКА. пристосуйте це до своїх потреб!
Роберваль Сена, 本 本

5

Я розробив спосіб замінити нормальну поведінку історії та створити окремі події 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, щоб ми могли зафіксувати наступну навігаційну подію історії.

Очевидно, вам доведеться утриматись від використання методів history.replaceState та history.pushState під час використання сценарію HistoryButtonOverride, інакше ви його зламаєте.


4

Через проблеми конфіденційності неможливо відключити кнопку "Назад" або перевірити історію користувача, але можна створити нові записи в цій історії без зміни сторінок. Щоразу, коли програма AJAX змінює стан, оновіть top.locationновий фрагмент URI .

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

Це створить новий запис у стеку історії браузера. Ряд бібліотек AJAX вже обробляє всі нудні деталі, такі як Really Simple History .


Про які інші нудні деталі мені потрібно подбати, окрім встановлення top.location?
Zack Burt

Оскільки подія "завантаження" не запускається, коли користувач натискає назад, вам потрібно щось активувати подію. Також існують деякі відмінності між браузерами. Хороша бібліотека AJAX setTimeout()містила б усі ці деталі, включаючи приклад in darkporter.
Matthew

2

Я роблю щось подібне. Я зберігаю масив із попередніми станами додатків.

Ініціюється як:

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

2
Здається, ви відтворюєте функціональність кнопки "Назад". Це цікаво, але, мабуть, не відповідь на питання ОП.
Лука

2

У моїй ситуації я хотів не допустити, щоб кнопка "Назад" надсилала користувача на останню сторінку, а замість цього вживала іншої дії. Використовуючи 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подія не запускалася. Залежно від вашої ситуації, це може спрацювати і для вас.


1

Кнопка "Назад" у браузерах зазвичай намагається повернутися до попередньої адреси. у 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('#', ''))
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.