window.location.hrefが変更されたときのイベント


88

ある時点で変更するサイトのGreasemonkeyスクリプトを書いていlocation.hrefます。

ページが変更されたwindow.addEventListenerときに(または同様の方法で)イベントを取得するにはどうすればよいwindow.location.hrefですか?また、新しい/変更されたURLを指すドキュメントのDOMにアクセスする必要があります。

タイムアウトとポーリングを含む他の解決策を見てきましたが、可能であればそれを避けたいと思います。


YouTubeの場合:javascript-
user2027 2920年

回答:


90

popstateイベント

popstateイベントは、アクティブな履歴エントリが変更されたときに発生します。[...] popstateイベントは、戻るボタンをクリックする(またはJavaScriptでhistory.back()を呼び出す)などのブラウザーアクションを実行することによってのみトリガーされます。

したがって、使用時にpopstateイベントをリッスンしてイベントを送信するだけで、変更に対してアクションを実行できます。popstatehistory.pushState()href

window.addEventListener('popstate', listener);

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

2
しかし、コンテンツが読み込まれる前にpopstateが起動しますか?
user2782001 2017年

4
コードの一部を制御できない場合は使用できませんpushState
NathanGouy20年

2
これは常に機能するとは限りません。発生するのはpopstateイベントに依存します。たとえば、あなたが歴史に戻ったり、前方押した場合にのみ、それがトリガされませんユーチューブ内をナビゲート
TrySpace

57

私は自分の拡張機能「GrabAnyMedia」でこのスクリプトを使用し、正常に動作します(youtubeケースのように

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);

};

どういうわけか、私はこのアプローチが好きです...少しRxJっぽい感じがします:)
Guntram

youtubeで機能する可能性のある唯一の解決策:[レポートのみ]次のコンテンツセキュリティポリシーディレクティブに違反しているため、「youtube.com/sw.js」からワーカーを作成することを拒否しました:「worker-src'none '」。
Simon Meusel 2018

MutationObserver

1
私のユースケースでは問題なく箱から出して作業しました、神のご加護を!このためのネイティブイベントがまだないのは迷惑です(popstateは私には機能しませんでした)が、ある日!window.addEventListener("load", () => {})ただし、window.onloadの代わりに使用します:)
SanchitBatra20年

この作品、でも拡張コンテキストでhistory.pushStateを検出
YangombiUmpakati

29

ポーリングを回避することはできません。hrefの変更に関するイベントはありません。

船外に出なければ、とにかくインターバルの使用はかなり軽いです。約50ミリ秒ごとにhrefをチェックしても、心配している場合はパフォーマンスに大きな影響はありません。


3
@AlexanderMills:幸いなことにpopstateとのこれらの新しいソリューションがありhashchangeます。
serv-inc

1
本当じゃない。
inf3rno 2018

@MartinZvarík2010年でも、アンロードイベントとマイクロまたはマクロタスクを使用して、新しいドキュメントをロードすることができました。
inf3rno 2018年

3
残念ながら、この答えは2020年でも正しいです。popstateはユーザーが進む/戻るボタン使用した場合にのみ発生し、 hashchangeはハッシュの変更に対してのみ発生します。あなたが変更に場所を引き起こしているコードを管理している場合を除き、あなたは🤷♀️投票に持っている
プエルトリコケーラー

14

onhashchange使用できる デフォルトのイベントがあります。

ここに文書化

そして、このように使用することができます:

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

window.onhashchange = locationHashChanged;

ブラウザがサポートしておらずoldURLnewURL次のようにバインドできる場合:

//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
ハッシュチェンジイベントは、URLに「#」記号で始まる部分がある場合にのみ送信されます。これは通常、アンカーリンクに使用されます。それ以外の場合は起動しません。
Fredrik_Macrobond 2018

1
window.addEventListener( "hashchange"、funcRef、false);
BishoyHanna20年

7

beforeUnloadを試しましたか?このイベントは、ページがナビゲーションリクエストに応答する直前に発生します。これには、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 2010

新しいDOMにもアクセスする必要があります。それを明確にするために、質問を更新しました。
ヨハン・ダーリン2010

OK-hash状況を考慮しなかった-それについて考えるだろう。新しいDOMにアクセスできることに関しては、新しいDOMがロードされる前にイベントが発生するため、(イベントハンドラーからこれにアクセスする限り)運が悪いことになります。新しいページのonloadイベントにロジックを組み込むことはできるかもしれませんが、そのページのすべてのロードに対してロジックを実行する必要があるかどうかの識別に関して同じ問題が発生する可能性があります。ページフローなど、達成しようとしていることについてもう少し詳しく教えてください。
belugabob 2010

onbeforeunloadイベントハンドラー内のlocation.hrefにアクセスしようとしましたが、ターゲットURLではなく元のURLが表示されます。
CMCDragonkai 2017

Beforeunloadはページのアンロードをキャンセルできるため、ナビゲーションをキャンセルしたくない場合は、unloadイベントの方が適しています。
inf3rno 2018


2

を変更するには2つの方法がありますlocation.hreflocation.href = "y.html"ページをリロードする書き込み、またはページをリロードしない履歴APIを使用できます。最近、最初の実験をたくさんしました。

子ウィンドウを開き、親ウィンドウから子ページの負荷をキャプチャすると、ブラウザが異なれば動作も大きく異なります。一般的なのは、古いドキュメントを削除して新しいドキュメントを追加することだけです。たとえば、readystatechangeやloadイベントハンドラーを古いドキュメントに追加しても効果はありません。ほとんどのブラウザはウィンドウオブジェクトからもイベントハンドラを削除しますが、唯一の例外はFirefoxです。Chrome with Karma runnerおよびFirefoxでは、次の場合、loadingreadyStateで新しいドキュメントをキャプチャできます。unload + next tick。したがって、たとえば、loadイベントハンドラーまたはreadystatechangeイベントハンドラーを追加したり、ブラウザーが新しいURIでページをロードしていることをログに記録したりできます。手動テストを使用するChrome(おそらくGreaseMonkeyも)およびOpera、PhantomJS、IE10、IE11では、読み込み状態で新しいドキュメントをキャプチャすることはできません。これらのブラウザunload + next tickでは、ページのロードイベントが発生する数百ミリ秒後にコールバックがコールバックを呼び出します。遅延は通常100〜300ミリ秒ですが、オペラsimetimeでは次のティックに750ミリ秒の遅延が発生するため、怖いです。したがって、すべてのブラウザで一貫した結果が必要な場合は、loadイベントの後でやりたいことを実行しますが、その前に場所が上書きされないという保証はありません。

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.