Как получить уведомление об изменениях истории через history.pushState?
Итак, что HTML5 вводит history.pushState
чтобы изменить историю браузеров, веб-сайты начинают использовать это в сочетании с Ajax вместо изменения идентификатора фрагмента URL.
к сожалению, это означает, что эти вызовы больше не могут быть обнаружены onhashchange
.
мой вопрос: есть ли надежный способ (hack? ;)) чтобы определить, когда веб-сайт использует history.pushState
? В спецификации ничего не говорится о событиях, которые возникают (в по крайней мере, я ничего не нашел).
Я попытался создать фасад и заменил window.history
С моим собственным объектом JavaScript, но он не имел никакого эффекта вообще.
дальнейших объяснений: я разрабатываю дополнение Firefox, которое должно обнаруживать эти изменения и действовать соответственно.
Я знаю, что был аналогичный вопрос несколько дней назад, который спросил, слушает ли некоторые событий DOM было бы эффективно, но я бы предпочел не полагаться на это, потому что эти события могут быть сгенерированы по разным причинам.
обновление:
вот jsfiddle (используйте Firefox 4 или Chrome 8), который показывает, что onpopstate
не срабатывает, когда pushState
называется (или я делаю что-то неправильно? Не стесняйтесь улучшать его!).
обновление 2:
другая (сторона) проблема в том, что window.location
не обновляется при использовании pushState
(но я читал об этом уже здесь, поэтому я думать.)
8 ответов
5.5.9.1 определения событий
на 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 материал.
и: ресурс: / / gre / модули / WebNavigationContent.js
поскольку вы спрашиваете о аддоне Firefox, вот код, который я должен работать. Используя unsafeWindow
is не рекомендуется и ошибки, когда 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 запускается только при выполнении действия браузера, такого как нажатие кнопки "Назад" (или история вызовов).back () в JavaScript)
нужно звонки.back () в trigeer WindowEventHandlers.onpopstate
Так insted из:
history.pushState(...)
do:
history.pushState(...)
history.pushState(...)
history.back()