pushState создает повторяющиеся записи истории и перезаписывает предыдущие


15

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

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

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

  1. Загрузите index.php (история будет: Index)

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

  3. Перейдите к search.php (история будет: Поиск, Поиск, Индекс)

  4. Перейдите к dashboard.php

И, наконец, вот что всплывет в моей истории (в порядке от самых последних до самых старых):

Приборная панель
Приборная панель
Приборная панель
Search
Index

Проблема заключается в том, что когда пользователь нажимает кнопки «вперед» или «назад», они либо перенаправляются на неправильную страницу, либо вынуждены нажимать несколько раз, чтобы вернуться назад один раз. Это, и не будет никакого смысла, если они пойдут и проверит свою историю.

Это то, что я до сих пор:

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

Вся помощь приветствуется,
ура.


Таким образом, вы хотите распространять изменение истории, только если цель отличается от последней записи?
— Алуан Хаддад

Это происходит на первый взгляд случайно? Или конкретные страницы всегда вызывают повторяющиеся записи в истории.
— SamVK

@AluanHaddad нет. Я хочу распространять изменение истории только в том случае, если есть изменение истории (т. Е. Когда пользователь перемещается по сайту). Как и в обычной ситуации ... (переход от индекса к профилю к поиску в StackOverflow вернул бы именно то, что было в моей истории)
— GROVER.

@SamVK Не имеет значения, на какой странице после перехода с начальной страницы загрузки (т. Е. Index.php) записи будут дублироваться.
— Гровер.

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

Ответы:


3

У вас есть обработчик onpopstate ?

Если да, то проверьте там также, если вы не настаиваете на истории. То, что некоторые записи удалены / заменены в списке истории, может быть большим признаком. В самом деле, посмотрите этот так ответ :

Имейте в виду, что history.pushState () устанавливает новое состояние как новейшее состояние истории. И window.onpopstate вызывается при навигации (назад / вперед) между состояниями, которые вы установили.

Поэтому не нажимайте pushState при вызове window.onpopstate, так как это установит новое состояние в качестве последнего состояния, а затем не к чему идти.

Однажды у меня была точно такая же проблема, как вы описали, но на самом деле это было вызвано тем, что я возвращался назад и вперед, чтобы попытаться понять ошибку, которая в конечном итоге вызовет обработчик popState. Из этого обработчика он будет вызывать history.push. Итак, в конце у меня также было несколько дублированных записей, а некоторые отсутствовали без какого-либо логического объяснения.

Я удалил вызов history.push, заменил его на history.replace после проверки некоторых условий и после того, как он заработал как брелок :)

РЕДАКТИРОВАТЬ -> СОВЕТ

Если вы не можете определить, какой код вызывает history.pushState:

Попробуйте перезаписать функции history.pushState и replaceState следующим кодом:

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;перед возобновлением. Таким образом, вы не пропустите ни одного файла history.pushState, а сможете подняться и найти код, который его вызывает :)


Я делаю, но это не толкает и не заменяет историю вообще: / просто отображает страницу
— GROVER.

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

Я проверил стек вызовов, и все, кажется, сделано правильно - но история не хочет работать. Почему Мозилла должна сделать все так сложно :(
— Гровер.

Спасибо :) А если вы попробуете на Chrome, какие у вас результаты?
— Bonjour123
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.