pushState, створюючи повторювані записи історії та перезаписуючи попередні


15

Я створив веб-додаток, який використовує методи history pushStateта replaceStateметоди для навігації по сторінках, а також оновлення історії.

Сам сценарій працює майже ідеально; він завантажить сторінки правильно та викине помилки на сторінці, коли їх потрібно буде кинути. Однак я помітив дивну проблему, коли pushStateв історію підштовхнуть декілька, дублюються записи (і замінять записи).

Наприклад, скажімо, я роблю наступне (по порядку):

  1. Завантажте index.php (історія буде: індекс)

  2. Перейдіть до profile.php (історія буде: Профіль, індекс)

  3. Перейдіть до search.php (історія буде: Пошук, Пошук, Індекс)

  4. Перейдіть до dashboard.php

І нарешті, ось що з’явиться в моїй історії (в порядку найсвіжішого до найдавнішого):

Індекс пошуку на приладовій панелі
приладової
панелі Індекс
пошуку

Проблема з цим полягає в тому, що коли користувач натискає кнопки вперед або назад, він або перенаправляється на неправильну сторінку, або доведеться натискати кілька разів, щоб один раз повернутися назад. Це, і не буде сенсу, якщо вони завітають і перевірять свою історію.

Ось що я маю досі:

var Traveller = function(){
    this._initialised = false;

    this._pageData = null;
    this._pageRequest = null;

    this._history = [];
    this._currentPath = null;
    this.abort = function(){
        if(this._pageRequest){
            this._pageRequest.abort();
        }
    };
    // initialise traveller (call replaceState on load instead of pushState)
    return this.init();
};

/*1*/Traveller.prototype.init = function(){
    // get full pathname and request the relevant page to load up
    this._initialLoadPath = (window.location.pathname + window.location.search);
    this.send(this._initialLoadPath);
};
/*2*/Traveller.prototype.send = function(path){
    this._currentPath = path.replace(/^\/+|\/+$/g, "");

    // abort any running requests to prevent multiple
    // pages from being loaded into the DOM
    this.abort();

    return this._pageRequest = _ajax({
        url: path,
        dataType: "json",
        success: function(response){
            // render the page to the dom using the json data returned
            // (this part has been skipped in the render method as it
            // doesn't involve manipulating the history object at all
            window.Traveller.render(response);
        }
    });
};
/*3*/Traveller.prototype.render = function(data){
    this._pageData = data;
    this.updateHistory();
};
/*4*/Traveller.prototype.updateHistory = function(){
    /* example _pageData would be:
    {
        "page": {
            "title": "This is a title",
            "styles": [ "stylea.css", "styleb.css" ],
            "scripts": [ "scripta.js", "scriptb.js" ]
        }
    }
    */
    var state = this._pageData;
    if(!this._initialised){
        window.history.replaceState(state, state.title, "/" + this._currentPath);
        this._initialised = true;
    } else {
        window.history.pushState(state, state.title, "/" + this._currentPath);  
    }
    document.title = state.title;
};

Traveller.prototype.redirect = function(href){
    this.send(href);
};

// initialise traveller
window.Traveller = new Traveller();

document.addEventListener("click", function(event){
    if(event.target.tagName === "a"){
        var link = event.target;
        if(link.target !== "_blank" && link.href !== "#"){
            event.preventDefault();
            // example link would be /profile.php
            window.Traveller.redirect(link.href);
        }
    }
});

Всяка допомога вдячна,
привіт.


Тож ви хочете лише поширити зміну історії, якщо ціль відрізняється від останнього запису?
Алуан Хаддад

Це відбувається, здавалося б, випадково? Або певні сторінки завжди є тими, які викликають повторювані записи історії.
СамВК

@AluanHaddad ні, я хочу розповсюджувати зміни історії лише в тому випадку, коли є зміна історії (тобто коли користувач пересувається по сайту). Подібно до того, що буде у звичайній ситуації ... (перехід від індексу до профілю для пошуку в StackOverflow поверне саме те, що в моїй історії)
GROVER.

@SamVK Не має значення, на якій сторінці після переходу з первинної сторінки завантаження (тобто. Index.php) записи дублюються.
ДЕРЖАВИ.

1
Ну, я не зовсім впевнений у цьому, тому пишу в коментарях. Я бачу, що ви додаєте речі з історії браузера у свою updateHistoryфункцію. Тепер, updateHistoryякщо ви ініціалізуєте Traveller ( window.Traveller = new Traveller();, constructor-> init-> send-> render-> updateHistory), redirectвам clickдзвонять двічі , потім також від EventListener. Я не перевіряв його, просто дикі здогадки, тому додаю це як коментар, а не відповідь.
Акшіт Арора

Відповіді:


3

Чи є у вас обробник на повісті ?

Якщо так, то також перевірте це, якщо ви не наполягаєте на історії. Те, що деякі записи видалено / замінено у списку історії, може бути великим знаком. Дійсно, дивіться цю відповідь :

Майте на увазі, що history.pushState () встановлює новий стан як найновіший стан історії. І window.onpopstate викликається під час навігації (назад / вперед) між установленими вами станами.

Тому не натискайте кнопку "Держава", коли викликається window.onpopstate, оскільки це встановить новий стан як останній стан, і тоді немає чого йти вперед.

У мене колись була точно така ж проблема, як ви описуєте, але насправді це було викликано тим, що я повертався назад і вперед, щоб спробувати зрозуміти помилку, яка в кінцевому підсумку спровокує обробник popState. Від цього обробника він буде потім називати history.push. Тож наприкінці у мене також було кілька дублюваних записів, а деякі відсутні, без логічного пояснення.

Я видалив дзвінок на history.push, замінив його на history.seplace після перевірки деяких умов, і після цього він працював як шарм :)

РЕДАКТУВАТИ -> ПОРАД

Якщо ви не можете знайти, який код викликає історію.pushState:

Спробуйте, перезаписавши функцію history.pushState і замінітьState наступним кодом:

window.pushStateOriginal = window.history.pushState.bind(window.history);
window.history.pushState = function () {
    var args = Array.prototype.slice.call(arguments, 0);
    let allowPush  = true;
    debugger;
    if (allowPush ) {
        window.pushStateOriginal(...args);
    }
}
//the same for replaceState
window.replaceStateOriginal = window.history.replaceState.bind(window.history);
window.history.replaceState = function () {
    var args = Array.prototype.slice.call(arguments, 0);
    let allowReplace  = true;
    debugger;
    if (allowReplace) {
        window.replaceStateOriginal(...args);
    }
}

Потім кожен раз, коли точки переривання запускаються, переглядайте стек викликів.

У консолі, якщо ви хочете запобігти pushState, просто введіть allowPush = false;або allowReplace = false;до відновлення. Таким чином, ви не збираєтесь пропустити жодну історію.pushState, і можете піднятися вгору і знайти код, який його викликає :)


Я це роблю, але це зовсім не підштовхує та не замінює історію: / просто робить сторінку
GROVER.

Дійсно дивно. Спробуйте перезаписати функцію history.push наступним кодом: const hP = history.pushState history.pushState = (loc) => {відладчик; hP (loc)} і зробіть те саме для history.replaceState. Потім щоразу, коли точки прориву запускаються, перегляньте стек викликів
Bonjour123

Я перевірив стек викликів і, здається, все зроблено правильно - все ж історія не хоче працювати. Чому Mozilla має зробити все настільки важким :(
GROWER.

Дякую :) А якщо ви спробуєте на Chrome, які результати у вас є?
Bonjour123
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.