window.location.href 변경시 이벤트


82

어느 시점에서 수정하는 사이트에 대한 Greasemonkey 스크립트를 작성 중 location.href입니다.

페이지 window.addEventListenerwindow.location.href변경 될 때 어떻게 이벤트를받을 수 있습니까 ( 또는 이와 유사한 것을 통해 ) ? 또한 새 / 수정 된 URL을 가리키는 문서의 DOM에 대한 액세스가 필요합니다.

시간 초과 및 폴링과 관련된 다른 솔루션을 보았지만 가능하면 피하고 싶습니다.


답변:


87

popstate 이벤트 :

popstate 이벤트는 활성 히스토리 항목이 변경 될 때 시작됩니다. [...] popstate 이벤트는 뒤로 버튼을 클릭하거나 JavaScript에서 history.back ()을 호출하는 것과 같은 브라우저 작업을 수행하는 경우에만 트리거됩니다.

따라서 popstate이벤트를 수신하고 popstate사용할 때 이벤트를 보내는 history.pushState()것만으로도 href변경 에 대한 조치를 취할 수 있습니다.

window.addEventListener('popstate', listener);

const pushUrl = (href) => {
  history.pushState({}, '', href);
  window.dispatchEvent(new Event('popstate'));
};

2
그러나 콘텐츠가로드되기 전에 popstate가 실행됩니까?
user2782001

4
코드 조각을 제어 할 수 없으면 사용할 수 없습니다.pushState
Nathan Gouy

2
이것은 항상 작동하는 것은 아니며 popstate 이벤트에 의존합니다. 유튜브 내에서 탐색 예를 들어 그것을하지 않습니다 트리거, 당신은 역사를 뒤로 또는 앞으로 누를 경우에만
TrySpace

52

내 확장 프로그램 "Grab Any Media"에서이 스크립트를 사용하고 잘 작동합니다 ( 예 : youtube case ).

var oldHref = document.location.href;

window.onload = function() {

    var
         bodyList = document.querySelector("body")

        ,observer = new MutationObserver(function(mutations) {

            mutations.forEach(function(mutation) {

                if (oldHref != document.location.href) {

                    oldHref = document.location.href;

                    /* Changed ! your code here */

                }

            });

        });

    var config = {
        childList: true,
        subtree: true
    };

    observer.observe(bodyList, config);

};

어떻게 든, 나는이 방법처럼 ... :) 조금 RxJs 틱 느낌
트람

YouTube에서 효과가있을 수있는 유일한 솔루션 : [신고 전용] ' youtube.com/sw.js ' 에서 작업자 생성이 거부되었습니다. 콘텐츠 보안 정책 지침 'worker-src'none ''을 위반했기 때문입니다.
Simon Meusel

MutationObserver

1
내 사용 사례에 대한 문제없이 즉시 작업했습니다. 신의 축복이 있기를! 아직 이것에 대한 네이티브 이벤트가 없다는 것은 성가신 일이지만 (popstate는 나를 위해 작동하지 않았습니다) 언젠가! 나는 window.addEventListener("load", () => {})window.onload 대신 사용할 것입니다 :)
Sanchit Batra

이 작동하고 확장 컨텍스트에서도 history.pushState를 감지합니다
YangombiUmpakati

29

폴링을 피할 수 없으며 href 변경에 대한 이벤트가 없습니다.

배 밖으로 나가지 않으면 간격을 사용하는 것은 어쨌든 매우 가볍습니다. 50ms 정도마다 href를 확인하는 것이 걱정되는 경우 성능에 큰 영향을 미치지 않습니다.


3
@AlexanderMills : 다행스럽게도 popstate및의 새로운 솔루션이 hashchange있습니다.
serv-inc

이것은 사실이 아닙니다.
inf3rno

@ MartinZvarík 2010 년에도 언로드 이벤트와 마이크로 또는 매크로 작업을 사용하여 새 문서를로드 할 수있었습니다.
inf3rno

3
불행히도이 답변은 2020 년에도 정확합니다. popstate는 사용자가 앞으로 / 뒤로 버튼 을 사용할 때만 실행되고 해시 변경은 해시 변경에 대해서만 실행됩니다. 위치를 변경하는 코드를 제어 할 수없는 경우 🤷‍♀️
Rico Kahler

12

onhashchange사용할 수 있는 기본 이벤트가 있습니다.

여기에 문서화

다음과 같이 사용할 수 있습니다.

function locationHashChanged( e ) {
    console.log( location.hash );
    console.log( e.oldURL, e.newURL );
    if ( location.hash === "#pageX" ) {
        pageX();
    }
}

window.onhashchange = locationHashChanged;

브라우저가 지원하지 않는 경우 oldURL그리고 newURL당신은 이런 식으로 바인딩 할 수 있습니다 :

//let this snippet run before your hashChange event binding code
if( !window.HashChangeEvent )( function() {
    let lastURL = document.URL;
    window.addEventListener( "hashchange", function( event ) {
        Object.defineProperty( event, "oldURL", { enumerable: true, configurable: true, value: lastURL } );
        Object.defineProperty( event, "newURL", { enumerable: true, configurable: true, value: document.URL } );
        lastURL = document.URL;
    } );
} () );

8
Hashchange 이벤트는 URL에 일반적으로 앵커 링크에 사용되는 '#'기호로 시작하는 부분이있는 경우에만 전송됩니다. 그렇지 않으면 발사되지 않습니다.
Fredrik_Macrobond

1
window.addEventListener ( "hashchange", funcRef, false);
Bishoy Hanna

7

전에 시도해 보셨습니까? 이 이벤트는 페이지가 탐색 요청에 응답하기 직전에 발생하며 여기에는 href의 수정이 포함되어야합니다.

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
    <HTML>
    <HEAD>
    <TITLE></TITLE>
    <META NAME="Generator" CONTENT="TextPad 4.6">
    <META NAME="Author" CONTENT="?">
    <META NAME="Keywords" CONTENT="?">
    <META NAME="Description" CONTENT="?">
    </HEAD>

         <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js" type="text/javascript"></script>
            <script type="text/javascript">
            $(document).ready(function(){
                $(window).unload(
                        function(event) {
                            alert("navigating");
                        }
                );
                $("#theButton").click(
                    function(event){
                        alert("Starting navigation");
                        window.location.href = "http://www.bbc.co.uk";
                    }
                );

            });
            </script>


    <BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#FF0000" VLINK="#800000" ALINK="#FF00FF" BACKGROUND="?">

        <button id="theButton">Click to navigate</button>

        <a href="http://www.google.co.uk"> Google</a>
    </BODY>
    </HTML>

그러나 스크립트 때문이든 누군가가 링크를 클릭했는지 여부에 관계없이 페이지에서 벗어날 때마다 이벤트가 실행된다는 점에 유의 하십시오. 당신의 진정한 도전은 이벤트가 시작되는 다양한 이유를 감지하는 것입니다. (이것은 당신의 논리에 중요한 경우)


해시 변경이 페이지 다시로드를 포함하지 않기 때문에 이것이 작동하는지 확실하지 않습니다.
samandmoore

새 DOM에 대한 액세스도 필요하며이를 명확히하기 위해 질문을 업데이트했습니다.
Johan Dahlin 2010-08-19

OK- hash상황을 고려하지 않았습니다-그 상황에 대해 생각할 것입니다. 새 DOM에 액세스 할 수 있다는 점은 새 DOM이로드되기 전에 이벤트가 실행되므로 운이 좋지 않습니다. 논리를 새 페이지의 onload 이벤트에 통합 할 수 있지만 해당 페이지의 모든로드에 대해 논리를 수행해야하는지 여부를 식별하는 것과 관련하여 동일한 문제가있을 수 있습니다. 페이지 흐름을 포함하여 달성하려는 작업에 대한 세부 정보를 제공 할 수 있습니까?
belugabob

onbeforeunload 이벤트 핸들러 내부의 location.href에 액세스하려고 시도했는데 대상 URL이 아닌 원래 URL이 표시됩니다.
CMCDragonkai

Beforeunload는 페이지 언로드를 취소 할 수 있으므로 탐색을 취소하지 않으려면 언로드 이벤트가 더 좋습니다.
inf3rno


2

.NET Framework를 변경하는 방법에는 두 가지가 있습니다 location.href. 어느 쪽이든 당신은 쓸 수있는 location.href = "y.html"페이지를 다시로드하거나 페이지를 다시로드하지 않는 역사의 API를 사용할 수있는. 최근에 처음으로 많이 실험했습니다.

자식 창을 열고 부모 창에서 자식 페이지의로드를 캡처하면 다른 브라우저가 매우 다르게 작동합니다. 유일한 공통점은 이전 문서를 제거하고 새 문서를 추가하는 것입니다. 예를 들어 이전 문서에 readystatechange 또는 load 이벤트 핸들러를 추가해도 아무런 효과가 없습니다. 대부분의 브라우저는 창 개체에서도 이벤트 처리기를 제거합니다. 유일한 예외는 Firefox입니다. Karma runner 및 Firefox가있는 Chrome에서 다음을 사용하는 경우 loading readyState에서 새 문서를 캡처 할 수 있습니다.unload + next tick. 따라서 예를 들어로드 이벤트 처리기 또는 readystatechange 이벤트 처리기를 추가하거나 브라우저가 새 URI로 페이지를로드하고 있음을 기록 할 수 있습니다. 수동 테스트 (GreaseMonkey도 가능)를 사용하는 Chrome과 Opera, PhantomJS, IE10, IE11에서는로드 상태에서 새 문서를 캡처 할 수 없습니다. 이러한 브라우저 unload + next tick에서 페이지의로드 이벤트가 발생하는 것보다 수백 msec 후에 콜백을 호출합니다. 지연은 일반적으로 100 ~ 300msec이지만 Opera simetime은 다음 틱에 대해 750msec 지연을 만들어 무섭습니다. 따라서 모든 브라우저에서 일관된 결과를 원하면로드 이벤트 후에 원하는 작업을 수행하지만 그 전에 위치가 재정의되지 않을 것이라는 보장은 없습니다.

var uuid = "win." + Math.random();
var timeOrigin = new Date();
var win = window.open("about:blank", uuid, "menubar=yes,location=yes,resizable=yes,scrollbars=yes,status=yes");


var callBacks = [];
var uglyHax = function (){
    var done = function (){
        uglyHax();
        callBacks.forEach(function (cb){
            cb();
        });
    };
    win.addEventListener("unload", function unloadListener(){
        win.removeEventListener("unload", unloadListener); // Firefox remembers, other browsers don't
        setTimeout(function (){
            // IE10, IE11, Opera, PhantomJS, Chrome has a complete new document at this point
            // Chrome on Karma, Firefox has a loading new document at this point
            win.document.readyState; // IE10 and IE11 sometimes fails if I don't access it twice, idk. how or why
            if (win.document.readyState === "complete")
                done();
            else
                win.addEventListener("load", function (){
                    setTimeout(done, 0);
                });
        }, 0);
    });
};
uglyHax();


callBacks.push(function (){
    console.log("cb", win.location.href, win.document.readyState);
    if (win.location.href !== "http://localhost:4444/y.html")
        win.location.href = "http://localhost:4444/y.html";
    else
        console.log("done");
});
win.location.href = "http://localhost:4444/x.html";

Firefox에서만 스크립트를 실행하는 경우 단순화 된 버전을 사용하고로드 상태에서 문서를 캡처 할 수 있습니다. 예를 들어 URI 변경을 기록하기 전에로드 된 페이지의 스크립트를 탐색 할 수 없습니다.

var uuid = "win." + Math.random();
var timeOrigin = new Date();
var win = window.open("about:blank", uuid, "menubar=yes,location=yes,resizable=yes,scrollbars=yes,status=yes");


var callBacks = [];
win.addEventListener("unload", function unloadListener(){
    setTimeout(function (){
        callBacks.forEach(function (cb){
            cb();
        });
    }, 0);
});


callBacks.push(function (){
    console.log("cb", win.location.href, win.document.readyState);
    // be aware that the page is in loading readyState, 
    // so if you rewrite the location here, the actual page will be never loaded, just the new one
    if (win.location.href !== "http://localhost:4444/y.html")
        win.location.href = "http://localhost:4444/y.html";
    else
        console.log("done");
});
win.location.href = "http://localhost:4444/x.html";

URI의 해시 부분을 변경하거나 히스토리 API를 사용하는 단일 페이지 응용 프로그램에 대해 이야기하는 경우 각각 hashchangepopstate창의 이벤트를 사용할 수 있습니다 . 동일한 페이지에 머물 때까지 역사에서 앞뒤로 이동하더라도 캡처 할 수 있습니다. 문서는 변경되지 않으며 페이지는 실제로 다시로드되지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.