Как получить уведомление об изменениях истории через историю.pushState?



Итак, теперь, когда HTML5 вводит history.pushState чтобы изменить историю браузеров, веб-сайты начинают использовать это в сочетании с Ajax вместо изменения идентификатора фрагмента URL.



к сожалению, это означает, что эти вызовы больше не могут быть обнаружены onhashchange.



мой вопрос: есть ли надежный способ (Хак? ;)) чтобы определить, когда веб-сайт использует history.pushState? В спецификации ничего не говорится о событиях, которые возникают (at по крайней мере, я ничего не мог найти).

Я попытался создать фасад и заменил window.history С моим собственным объектом JavaScript, но это не имело никакого эффекта вообще.



дальнейших объяснений: я разрабатываю дополнение Firefox, которое должно обнаруживать эти изменения и действовать соответственно.

Я знаю, что несколько дней назад был подобный вопрос, который спросил, слушают ли некоторые событий DOM было бы эффективно, но я бы предпочел не полагаться на это, потому что эти события могут быть сгенерированы по разным причинам.



обновление:



вот jsfiddle (используйте Firefox 4 или Chrome 8), который показывает, что onpopstate не срабатывает, когда pushState называется (или я делаю что-то неправильно? Не стесняйтесь, чтобы улучшить его!).



обновление 2:



другая (боковая) проблема заключается в том, что window.location не обновляется при использовании pushState (но я читал об этом уже здесь, поэтому я думать.)

551   8  

8 ответов:

5.5.9.1 определения событий

The popstate событие запускается в некоторых случаях при переходе к сеансу записи.

в соответствии с этим, нет никаких причин для popstate, чтобы быть уволены при использовании pushState. Но такое событие, как pushstate пригодится. Потому что history - это объект, вы должны быть осторожны с ним, но Firefox, кажется, хорошо в этом случае. Этот код работает просто отлично:

(function(history){
    var pushState = history.pushState;
    history.pushState = function(state) {
        if (typeof history.onpushstate == "function") {
            history.onpushstate({state: state});
        }
        // ... whatever else you want to do
        // maybe call onhashchange e.handler
        return pushState.apply(history, arguments);
    };
})(window.history);

ваш jsfiddle становится:

window.onpopstate = history.onpushstate = function(e) { ... }

вы можете обезьяна-патч window.history.replaceState точно так же.

Примечание: конечно, вы можете добавить onpushstate просто к глобальному объекту, и вы даже можете сделать его обрабатывать больше событий через add/removeListener

вы можете привязать к window.onpopstate событие?

https://developer.mozilla.org/en/DOM%3awindow.onpopstate

документы:

обработчик событий для popstate событие на окне.

событие popstate отправляется в окно каждый раз, когда активная история запись меняется. Если запись в истории активации был создан с помощью вызова в истории.pushState () или был затронут звонком история.replaceState(), государственная собственность на событие popstate по содержит копию записи истории государственный объект.

я использовал этот:

var _wr = function(type) {
    var orig = history[type];
    return function() {
        var rv = orig.apply(this, arguments);
        var e = new Event(type);
        e.arguments = arguments;
        window.dispatchEvent(e);
        return rv;
    };
};
history.pushState = _wr('pushState'), history.replaceState = _wr('replaceState');

window.addEventListener('replaceState', function(e) {
    console.warn('THEY DID IT AGAIN!');
});

это почти то же самое, что galambalazs сделал.

это обычно перебор, хотя. И это может не работать во всех браузерах. (Я забочусь только о моей версии моего браузера.)

(и он оставляет var _wr, Так что вы можете обернуть его или что-то еще. Меня это не волновало.)

Я думаю, что эта тема нуждается в более современное решение.

уверен nsIWebProgressListener тогда еще я удивлен, что никто не упомянул об этом.

из фреймскрипта (для совместимости с e10s):

let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebProgress);
webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_WINDOW | Ci.nsIWebProgress.NOTIFY_LOCATION);

тогда слушайте в onLoacationChange

onLocationChange: function onLocationChange(webProgress, request, locationURI, flags) {
       if (flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT

что будет, видимо, поймать все pushState по. Но есть замечание, предупредив, что это "также триггеры для pushState". Поэтому нам нужно сделать еще несколько фильтров здесь, чтобы убедиться, что это просто pushstate материал.

на основе: https://github.com/jgraham/gecko/blob/55d8d9aa7311386ee2dabfccb481684c8920a527/toolkit/modules/addons/WebNavigation.jsm#L18

и: resource://gre / modules / WebNavigationContent.js

Так как вы спрашиваете о Firefox аддон, вот код, который я должен работать. Используя unsafeWindow и не рекомендуется, и ошибки, когда pushState вызывается из клиентского скрипта после изменения:

отказано в доступе к истории свойств.pushState

вместо этого есть API под названием exportFunction что позволяет вводить функцию в window.history такой:

var pushState = history.pushState;

function pushStateHack (state) {
    if (typeof history.onpushstate == "function") {
        history.onpushstate({state: state});
    }

    return pushState.apply(history, arguments);
}

history.onpushstate = function(state) {
    // callback here
}

exportFunction(pushStateHack, unsafeWindow.history, {defineAs: 'pushState', allowCallbacks: true});

Ну, я вижу много примеров замены pushState собственность history но я не уверен, что это хорошая идея, я бы предпочел создать событие службы, основанное на подобном API для истории, чтобы вы могли контролировать не только состояние push, но и заменять состояние, а также открывать двери для многих других реализаций, не полагаясь на API глобальной истории. Пожалуйста, проверьте следующий пример:

function HistoryAPI(history) {
    EventEmitter.call(this);
    this.history = history;
}

HistoryAPI.prototype = utils.inherits(EventEmitter.prototype);

const prototype = {
    pushState: function(state, title, pathname){
        this.emit('pushstate', state, title, pathname);
        this.history.pushState(state, title, pathname);
    },

    replaceState: function(state, title, pathname){
        this.emit('replacestate', state, title, pathname);
        this.history.replaceState(state, title, pathname);
    }
};

Object.keys(prototype).forEach(key => {
    HistoryAPI.prototype = prototype[key];
});

Если вам нужны EventEmitter определение, приведенный выше код основан на событии NodeJS эмиттер: https://github.com/nodejs/node/blob/36732084db9d0ff59b6ce31e839450cd91a156be/lib/events.js.utils.inherits реализация может быть найдена здесь: https://github.com/nodejs/node/blob/36732084db9d0ff59b6ce31e839450cd91a156be/lib/util.js#L970

galambalazs это!--5--> обезьяна патчи window.history.pushState и window.history.replaceState, но по какой-то причине он перестал работать для меня. Вот альтернатива, которая не так хороша, потому что она использует опрос:

(function() {
    var previousState = window.history.state;
    setInterval(function() {
        if (previousState !== window.history.state) {
            previousState = window.history.state;
            myCallback();
        }
    }, 100);
})();

как стандартные состояния:

обратите внимание, что просто вызов истории.pushState() или истории.replaceState () не вызовет событие popstate. Событие popstate запускается только при выполнении действия браузера, такого как нажатие кнопки "Назад" (или вызов истории.назад() в JavaScript)

нужно звонки.назад () к trigeer WindowEventHandlers.onpopstate

Так вместо:

history.pushState(...)

do:

history.pushState(...)
history.pushState(...)
history.back()

Comments

    Ничего не найдено.