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

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

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

Мой вопрос: есть ли надежный способ (взломать?;)), чтобы определить, когда веб-сайт использует history.pushState

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

Update:

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

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

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

121 голос | спросил Felix Kling 31 FriEurope/Moscow2010-12-31T15:31:42+03:00Europe/Moscow12bEurope/MoscowFri, 31 Dec 2010 15:31:42 +0300 2010, 15:31:42

9 ответов


0
  

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

ответил gblazex 3 Jpm1000000pmMon, 03 Jan 2011 16:59:56 +030011 2011, 16:59:56
0

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

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

Из документов:

  

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

     

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

ответил stef 3 Jam1000000amMon, 03 Jan 2011 02:18:10 +030011 2011, 02:18:10
0

Раньше я использовал это:

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 сделал.

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

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

ответил Rudie 4 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowThu, 04 Sep 2014 23:54:18 +0400 2014, 23:54:18
0

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

Я уверен, что 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.

На основании: .com /jgraham /геккон /BLOB /55d8d9aa7311386ee2dabfccb481684c8920a527 /инструментарий /модули /аддонов /WebNavigation.jsm # L18

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

ответил Noitidart 15 +03002015-10-15T04:06:57+03:00312015bEurope/MoscowThu, 15 Oct 2015 04:06:57 +0300 2015, 04:06:57
0

ответ galambalazs исправления обезьян 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);
})();
ответил Flimm 3 J0000006Europe/Moscow 2015, 16:34:34
0

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

  

В доступе запрещено свойство history.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});
ответил nathancahill 28 AM000000120000000531 2015, 00:34:05
0

Ну, я вижу много примеров замены свойства 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/367320ffe/50/31/15/50/1606506b6 >. Реализацию utils.inherits можно найти здесь: https://github.com/nodejs/node/blob/36732084db9d0ff59b6ce31e839450cd91a156be/lib/util.js#L970

ответил Victor Queiroz 4 62017vEurope/Moscow11bEurope/MoscowSat, 04 Nov 2017 16:14:58 +0300 2017, 16:14:58
0

Наконец-то нашел «правильный» способ сделать это! Требуется добавить привилегию к вашему расширению и использовать фоновую страницу (не только скрипт содержимого), но это работает.

Событие, которое вам нужно, это browser.webNavigation.onHistoryStateUpdated , который запускается, когда страница использует history API для изменения URL. Он срабатывает только для сайтов, к которым у вас есть разрешение, и вы также можете использовать URL-фильтр для дальнейшего сокращения спама, если вам это нужно. Для этого требуется разрешение webNavigation (и, конечно, разрешение хоста для соответствующего домена (доменов)).

Обратный вызов события получает идентификатор вкладки, URL, по которому осуществляется переход, и другие подобные сведения. Если вам нужно выполнить действие в сценарии содержимого на этой странице при возникновении события, либо вставьте соответствующий сценарий непосредственно с фоновой страницы, либо попросите сценарий содержимого открыть port к фоновой странице при ее загрузке, сохраните фоновую страницу, сохраните этот порт в коллекции, проиндексированной по идентификатору вкладки, и отправьте сообщение через соответствующий порт (от фонового сценария к сценарию содержимого). когда происходит событие.

ответил CBHacking 16 Jam1000000amWed, 16 Jan 2019 04:34:10 +030019 2019, 04:34:10
0

Стандартно заявляет:

  

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

нам нужно вызвать history.back () для триггера WindowEventHandlers.onpopstate

Так сделано:

history.pushState(...)

делать:

history.pushState(...)
history.pushState(...)
history.back()
ответил user2360102 10 FebruaryEurope/MoscowbFri, 10 Feb 2017 13:42:02 +0300000000pmFri, 10 Feb 2017 13:42:02 +030017 2017, 13:42:02

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132