[javascript] history.pushState를 통해 히스토리 변경 사항에 대한 알림을받는 방법은 무엇입니까?

이제 HTML5 history.pushState가 브라우저 기록을 변경하기 위해 도입 했으므로 웹 사이트는 URL의 조각 식별자를 변경하는 대신 Ajax와 함께 이것을 사용하기 시작합니다.

안타깝게도이 전화를 더 이상 감지 할 수 없습니다 onhashchange.

내 질문은 : 웹 사이트가 history.pushState언제 사용되는지 감지하는 신뢰할 수있는 방법이 있습니까? 사양에는 발생한 이벤트에 대해 언급되어 있지 않습니다 (적어도 아무것도 찾을 수 없습니다).
파사드를 만들려고했는데 window.history내 고유의 JavaScript 객체 로 바뀌 었지만 전혀 효과가 없었습니다.

추가 설명 : 이 변경 사항을 감지하고 그에 따라 행동 해야하는 Firefox 추가 기능을 개발 중입니다.
며칠 전에 일부 DOM 이벤트를 듣는 것이 효과적 일지에 대한 비슷한 질문이 있었지만 이러한 이벤트가 여러 가지 이유로 생성 될 수 있기 때문에 그에 의존하지 않을 것입니다.

최신 정보:

다음은 호출 onpopstate될 때 트리거되지 않는 것을 보여주는 jsfiddle (Firefox 4 또는 Chrome 8 사용)입니다 pushState(또는 내가 잘못하고 있습니까? 자유롭게 개선하십시오!).

업데이트 2 :

또 다른 (측면) 문제는 window.location사용할 때 업데이트되지 않는다는 것 입니다 pushState(그러나 이미 여기에 대해 읽었습니다).



답변

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


답변

나는 이것을 사용했다 :

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므로 랩이나 무언가를 감싸고 싶을 수도 있습니다. 나는 그것에 대해 신경 쓰지 않았습니다.)


답변

마침내 이것을하는 “올바른”방법을 찾았습니다! 확장 프로그램에 권한을 추가하고 콘텐츠 스크립트뿐만 아니라 백그라운드 페이지를 사용해야하지만 작동합니다.

원하는 이벤트 browser.webNavigation.onHistoryStateUpdated는 페이지에서 historyAPI를 사용하여 URL을 변경하면 시작됩니다. 액세스 권한이있는 사이트에 대해서만 실행되며, 필요한 경우 URL 필터를 사용하여 스팸을 더욱 줄일 수 있습니다. webNavigation권한 (및 관련 도메인에 대한 호스트 권한) 이 필요합니다 .

이벤트 콜백은 탭 ID, “탐색중인”URL 및 기타 세부 정보를 가져옵니다. 이벤트가 발생할 때 해당 페이지의 컨텐츠 스크립트에서 조치를 취해야하는 경우 백그라운드 페이지에서 직접 관련 스크립트를 삽입하거나 컨텐츠 스크립트 port가로드 될 때 백그라운드 페이지를 엽니 다 . 백그라운드 페이지를 저장하십시오. 컬렉션의 해당 포트를 탭 ID로 색인화하고 이벤트가 발생할 때 관련 포트 (백그라운드 스크립트에서 콘텐츠 스크립트로)를 통해 메시지를 보냅니다.


답변

당신은 window.onpopstate이벤트에 바인딩 할 수 있습니까?

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

문서에서 :

윈도우의 popstate 이벤트를위한 이벤트 핸들러.

활성 내역 항목이 변경 될 때마다 popstate 이벤트가 창에 전달됩니다. 활성화 된 히스토리 항목이 history.pushState ()에 대한 호출로 작성되었거나 history.replaceState ()에 대한 호출의 영향을받는 경우, popstate 이벤트의 state 특성에는 히스토리 항목의 상태 오브젝트 사본이 포함됩니다.


답변

Firefox 애드온에 대해 질문하고 있으므로 여기에 작동해야 할 코드가 있습니다. 사용 unsafeWindow더 이상 권장되지 않으며 수정 후 클라이언트 스크립트에서 pushState를 호출하면 오류가 발생합니다.

속성 기록에 대한 액세스 권한이 거부되었습니다.

대신 다음 과 같이 함수를 삽입 할 수있는 exportFunction 이라는 API 가 있습니다 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});


답변

galambalazs의 답변 원숭이 패치 window.history.pushStatewindow.history.replaceState어떤 이유로 든 나를 위해 일을 중단했습니다. 다음은 폴링을 사용하기 때문에 좋지 않은 대안입니다.

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


답변

이 주제에는보다 현대적인 솔루션이 필요하다고 생각합니다.

나는 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를 잡을 것입니다. 그러나 “ALSO는 pushState를 트리거합니다”라는 주석 경고가 있습니다. 따라서 여기서 푸시 스테이트 상태가되도록 필터링을 더 수행해야합니다.

기반 : https://github.com/jgraham/gecko/blob/55d8d9aa7311386ee2dabfccb481684c8920a527/toolkit/modules/addons/WebNavigation.jsm#L18

그리고 : resource : //gre/modules/WebNavigationContent.js