JavaScriptでハッシュ後にURLが変更されたかどうかを検出する方法


160

JavaScriptでURLが変更されたかどうかを確認するにはどうすればよいですか?たとえば、AJAXを使用するGitHubのようなWebサイトは、#記号の後にページ情報を追加して、ページをリロードせずに一意のURLを作成します。このURLが変更されたかどうかを検出する最良の方法は何ですか?

  • されるonloadイベントが再び呼ばれますか?
  • URLのイベントハンドラーはありますか?
  • または、変更を検出するためにURLを毎秒チェックする必要がありますか?

1
将来の訪問者にとって、2018年の@aljgomによる新しい回答が最良の解決策です:stackoverflow.com/a/52809105/151503
Redzarf

回答:


106

最新のブラウザ(IE8 +、FF3.6 +、Chrome)では、でhashchangeイベントを聞くだけwindowです。

一部の古いブラウザでは、継続的にチェックするタイマーが必要location.hashです。jQueryを使用している場合は、まさにそれを行うプラグインがあります


132
私が理解しているように、これは#記号(したがってイベント名)の後のパーツの変更に対してのみ機能しますか?また、質問のタイトルからわかるように、完全なURLの変更ではありません。
NPC 2014年

13
@NPC完全なURL変更のハンドラー(アンカータグなし)?
Neha Choudhary 2014

タイムアウトイベントはほとんど必要ありません。チェックにはマウスイベントとキーボードイベントを使用してください。
Sjeiti

ハッシュではなくパスが変更された場合はどうなりますか?
SuperUberDuper

@SuperUberDuperユーザーがナビゲーションを開始した、リンクをクリックしたなどの理由でパスが変更された場合、beforeunloadイベントのみが表示されます。コードがURLの変更を開始した場合、それが最もよくわかります。
phihag

92

locationchangeイベントリスナーを追加できるようにしたいと思いました。以下の変更後、次のようにできるようになります

window.addEventListener('locationchange', function(){
    console.log('location changed!');
})

対照的window.addEventListener('hashchange',()=>{})に、URLのハッシュタグの後の部分が変更された場合にのみ起動し、window.addEventListener('popstate',()=>{})常に機能するとは限りません。

この修正は、クリスチャンの答えに似ていますに、いくつかの機能を追加するために履歴オブジェクトを変更します。

デフォルトでpopstateは、イベントはありますが、、、pushstateおよびのイベントはありませんreplacestate

この修正これらの三つの機能すべての火災のカスタムようにlocationchangeも、あなたが使用するイベント、およびpushstateおよびreplacestateイベントあなたがそれらを使用したい場合は:

/* These are the modifications: */
history.pushState = ( f => function pushState(){
    var ret = f.apply(this, arguments);
    window.dispatchEvent(new Event('pushstate'));
    window.dispatchEvent(new Event('locationchange'));
    return ret;
})(history.pushState);

history.replaceState = ( f => function replaceState(){
    var ret = f.apply(this, arguments);
    window.dispatchEvent(new Event('replacestate'));
    window.dispatchEvent(new Event('locationchange'));
    return ret;
})(history.replaceState);

window.addEventListener('popstate',()=>{
    window.dispatchEvent(new Event('locationchange'))
});

2
すばらしい、必要なものありがとう
eslamb

ありがとう、本当に役に立ちました!
Thomas Lang

1
IEでこれを行う方法はありますか?それはサポートしていません=>
joshuascotton

1
@joshuacotton =>はアロー関数です。f=> function fname(){...}をfunction(f){return function fname(){...}}で置き換えることができます
aljgom

1
これは非常に役立ち、魅力のように機能します。これは受け入れられる答えになるはずです。
Kunal Parekh

77

このコードを使用

window.onhashchange = function() { 
     //code  
}

jQuery

$(window).bind('hashchange', function() {
     //code
});

7
これは答えとしてマークする必要があります。それは1行で、ブラウザイベントモデルを使用し、無限のリソース消費タイムアウトに依存しません
Nick Mitchell

1
これは私の意見では、ここでの最良の答えです。プラグインなし、最小限のコード
agDev

14
Slackによって実装されたものなど、非常に人気があると思われる非ハッシュURLの変更には機能しません
NycCompSci

26
これがURLにハッシュがあるときにのみトリガーされる場合、これはどのように最良の答えですか?
アーロン

1
他のものが同様にリッスンしたい場合に備えて、onhashchange値を直接置き換えるのではなく、addEventListenerを使用する必要があります。
壊れた

55

少し調べてから編集:

どういうわけか、私はMozillaのドキュメントにあるドキュメントにだまされているようです。popstateイベント(およびそのコールバック関数がonpopstate)されているトリガされないときはいつでもpushState()、またはreplaceState()コードで呼び出されます。したがって、元の答えはすべての場合に適用されるわけではありません。

ただし、@ alpha123に従って関数をモンキーパッチすることでこれを回避する方法があります

var pushState = history.pushState;
history.pushState = function () {
    pushState.apply(history, arguments);
    fireEvents('pushState', arguments);  // Some event-handling function
};

元の答え

この質問のタイトルが「URLの変更を検出する方法」であるとすると、(ハッシュアンカーだけでなく)完全なパスがいつ変更されるかを知りたい場合は、popstateイベントをリッスンできます。

window.onpopstate = function(event) {
  console.log("location: " + document.location + ", state: " + JSON.stringify(event.state));
};

Mozillaドキュメントのpopstateのリファレンス

現在(2017年1月)、世界中のブラウザの92%からpopstateサポートされています。


11
2018
ヤギ

1
これは私の使用例では機能しましたが、@ goatが言うように、これに対するネイティブサポートがないとは信じられません...
wasddd_

1
どんな議論?どうすればfireEventsを設定できますか?
SeanMC

2
これも多くの場合信頼できないことに注意してください。たとえば、Amazonのさまざまな製品バリエーション(価格の下のタイル)をクリックしても、URLの変更は検出されません。
thdoan

2
これは、location.back()を使用しない場合、localhost / fooからlocalhost /
baa

53

jquery(およびプラグイン)を使用すると、次のことができます

$(window).bind('hashchange', function() {
 /* things */
});

http://benalman.com/projects/jquery-hashchange-plugin/

そうでない場合は、yes、setIntervalを使用して、ハッシュイベント(window.location.hash)の変更を確認する必要があります。

更新!簡単なドラフト

function hashHandler(){
    this.oldHash = window.location.hash;
    this.Check;

    var that = this;
    var detect = function(){
        if(that.oldHash!=window.location.hash){
            alert("HASH CHANGED - new has" + window.location.hash);
            that.oldHash = window.location.hash;
        }
    };
    this.Check = setInterval(function(){ detect() }, 100);
}

var hashDetection = new hashHandler();

(window.location)の変更を検出して処理できますか?(jqueryなし)
BergP

6
あなたプレーンなJavaScriptのリスナーを使用することができます@BergP、: window.addEventListener("hashchange", hashChanged);
ロン・

1
そのような短い時間間隔はアプリに適していますか?つまり、ブラウザはdetect()関数の実行で忙しすぎませんか?
Hasib Mahmud 2014

4
@HasibMahmud、そのコードは100msごとに1つの等式チェックを行っています。1ms未満で500の等式チェックを実行できることをブラウザでベンチマークしました。そのコードは私の処理能力の1/50000を使用しています。あまり心配しません。
z5h 2015年

24

ハッシュ変更イベントリスナーを追加します。

window.addEventListener('hashchange', function(e){console.log('hash changed')});

または、すべてのURL変更をリッスンするには:

window.addEventListener('popstate', function(e){console.log('url changed')});

window.onhashchangeには1つしか存在できず、他の誰かのコードを上書きする可能性があるため、これは以下のコードのようなものよりも優れています。

// Bad code example

window.onhashchange = function() { 
     // Code that overwrites whatever was previously in window.onhashchange  
}

1
ポップ状態は、1つの状態ではなく、1つの状態をポップしたときにのみトリガーされます
SeanMC '31年

8

この解決策は私にとってうまくいきました:

var oldURL = "";
var currentURL = window.location.href;
function checkURLchange(currentURL){
    if(currentURL != oldURL){
        alert("url changed!");
        oldURL = currentURL;
    }

    oldURL = window.location.href;
    setInterval(function() {
        checkURLchange(window.location.href);
    }, 1000);
}

checkURLchange();

18
これはやや初歩的な方法で、もっと高い目標を立てることができると思います。
Carles Alcolea 2017年

8
@CarlesAlcoleaはこれが古く感じられることに同意しますが、私の経験では、すべてのURL変更を100%キャッチする唯一の方法です。
Trev14 2017

5
@ahofmannは変更(コメントをされている必要があります編集に)示唆setIntervalsetTimeout:「それはcheckURLchange()秒ごとに新しいコールが作成されますので、しばらく後に停止するようにブラウザをもたらすのsetInterval()を使用したsetTimeout()一度しか呼び出されないため、正しい解決策です。」
ディビビサン

これはすべてのURL変更をキャッチする唯一の解決策でしたが、リソースを消費するため、他の解決策を探す必要がありました。
ReturnTable

3
またはsetTimeout、@ divibisanが提案するように使用する代わりにsetInterval、関数の外側を移動します。checkURLchange();また、オプションになります。
aristidesfl

4

古い質問ですが、ロケーションバープロジェクトは非常に便利です。

var LocationBar = require("location-bar");
var locationBar = new LocationBar();

// listen to all changes to the location bar
locationBar.onChange(function (path) {
  console.log("the current url is", path);
});

// listen to a specific change to location bar
// e.g. Backbone builds on top of this method to implement
// it's simple parametrized Backbone.Router
locationBar.route(/some\-regex/, function () {
  // only called when the current url matches the regex
});

locationBar.start({
  pushState: true
});

// update the address bar and add a new entry in browsers history
locationBar.update("/some/url?param=123");

// update the address bar but don't add the entry in history
locationBar.update("/some/url", {replace: true});

// update the address bar and call the `change` callback
locationBar.update("/some/url", {trigger: true});

これはnodeJ用であり、クライアント側で使用するにはbrowserifyを使用する必要があります。ね?
hack4mer 2018年

1
いいえ、そうではありません。ブラウザで動作
Ray Booysen

3

URLの変更をリッスンするには、以下を参照してください:

window.onpopstate = function(event) {
  console.log("location: " + document.location + ", state: " + JSON.stringify(event.state));
};

特定の条件の後でリスナーを停止/削除する場合は、このスタイルを使用します。

window.addEventListener('popstate', function(e) {
   console.log('url changed')
});

2

少しChromeの拡張を行っているときに、追加の問題で同じ問題に直面しました。時々、ページは変更されますがURLは変更されません。

たとえば、Facebookのホームページに移動し、[ホーム]ボタンをクリックします。ページを再読み込みしますが、URLは変更されません(1ページのアプリスタイル)。

99%の確率で、Angular、React、Vueなどのフレームワークからこれらのイベントを取得できるようにWebサイトを開発しています。

しかし、私のChrome拡張機能(バニラJSの場合)では、「ページの変更」ごとにトリガーされるイベントをリッスンする必要がありました。これは、通常、URLの変更によってキャッチできますが、そうでない場合もあります。

私の自家製の解決策は次のとおりでした:

listen(window.history.length);
var oldLength = -1;
function listen(currentLength) {
  if (currentLength != oldLength) {
    // Do your stuff here
  }

  oldLength = window.history.length;
  setTimeout(function () {
    listen(window.history.length);
  }, 1000);
}

つまり、基本的にはウィンドウ履歴に適用されるleoneckertソリューションであり、単一ページアプリでページが変更されると変更されます。

ロケット科学ではありませんが、ここでは整数の等価性のみをチェックし、より大きなオブジェクトやDOM全体ではないことを考えると、私が見つけた最もクリーンなソリューションです。


あなたの解決策はシンプルで、Chrome拡張機能に対して非常にうまく機能します。長さの代わりにYouTube動画IDを使用することをお勧めします。 stackoverflow.com/a/3452617/808901
Rod Lima

2
listen(xy)を呼び出すたびに新しいIntervalが作成され、結果として数千の間隔が生じるため、setIntervalを使用しないでください。
StefChäser19年

1
あなたが正しいと私は気づいた後、私の投稿を変更しなかった、それを編集します。昔は、RAMリークが原因でGoogle Chromeがクラッシュすることさえありました。コメントをありがとう
Alburkerk

window.history最大長は50です(少なくともChrome 80以降)。それ以降はwindow.history.length常に50を返します。その場合、このメソッドは変更を認識できません。
コリンクロール

1

jQuery unload関数を見てください。それはすべてのものを処理します。

https://api.jquery.com/unload/

アンロードイベントは、ユーザーがページから移動したときにウィンドウ要素に送信されます。これは、多くのことの1つを意味する可能性があります。ユーザーはリンクをクリックしてページを離れるか、アドレスバーに新しいURLを入力した可能性があります。進むボタンと戻るボタンでイベントがトリガーされます。ブラウザーウィンドウを閉じると、イベントがトリガーされます。ページのリロードでも、最初にアンロードイベントが作成されます。

$(window).unload(
    function(event) {
        alert("navigating");
    }
);

2
コンテンツがAjaxされている場合、ウィンドウがアンロードされなくてもURLが変更される場合があります。このスクリプトはURLの変更を検出しませんが、URLの変更ごとにウィンドウをアンロードするユーザーにとっては役立つ場合があります。
デボラ2016年

@ranbuch質問と同様に、これは単一ページアプリケーションではないページにのみ固有であり、このイベントはURLの変更ではなく、アンロードウィンドウイベントのみを監視しています。
ncubica 2016年

0
window.addEventListener("beforeunload", function (e) {
    // do something
}, false);

11
アンロードイベントはトリガーされないため、これは単一ページアプリケーションのコンテキストでは機能しません
ncubica

0

以下の答えはここから来ます(古いjavascript構文(矢印機能なし、IE 10+をサポート)):https : //stackoverflow.com/a/52809105/9168962

(function() {
  if (typeof window.CustomEvent === "function") return false; // If not IE
  function CustomEvent(event, params) {
    params = params || {bubbles: false, cancelable: false, detail: null};
    var evt = document.createEvent("CustomEvent");
    evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
    return evt;
  }
  window.CustomEvent = CustomEvent;
})();

(function() {
  history.pushState = function (f) {
    return function pushState() {
      var ret = f.apply(this, arguments);
      window.dispatchEvent(new CustomEvent("pushState"));
      window.dispatchEvent(new CustomEvent("locationchange"));
      return ret;
    };
  }(history.pushState);
  history.replaceState = function (f) {
    return function replaceState() {
      var ret = f.apply(this, arguments);
      window.dispatchEvent(new CustomEvent("replaceState"));
      window.dispatchEvent(new CustomEvent("locationchange"));
      return ret;
    };
  }(history.replaceState);
  window.addEventListener("popstate", function() {
    window.dispatchEvent(new CustomEvent("locationchange"));
  });
})();

0

これを行う別の簡単な方法は、クリックイベントを追加し、ページ上のアンカータグにクラス名を使用して、クリックされたことを検出することです。これで、window.location.hrefを使用してURLデータを取得できます。を使用して、サーバーへのajaxリクエストを実行できます。シンプルで簡単。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.