Chromeでのページの読み込み時のポップステート


94

WebアプリにHistory APIを使用していますが、1つの問題があります。ページの一部の結果を更新するためにAjax呼び出しを行い、history.pushState()を使用して、ページをリロードせずにブラウザーのロケーションバーを更新します。次に、もちろん、戻るボタンがクリックされたときに以前の状態を復元するためにwindow.popstateを使用します。

問題はよく知られています— ChromeとFirefoxはそのpopstateイベントを別の方法で扱います。Firefoxは初回ロード時に起動しませんが、Chromeは起動します。読み込み時にまったく同じ結果を更新するだけなので、読み込み時にイベントを発生させないように、Firefoxスタイルにしたいと思います。History.jsを使用する以外に回避策はありますか?私がそれを使用したくない理由は、それ自体が非常に多くのJSライブラリを必要とすることです。また、すでに多くのJSを含むCMSに実装する必要があるので、入れているJSを最小限にしたいと思います。 。

したがって、Chromeがロード時に「popstate」を起動しないようにする方法があるかどうか、または、すべてのライブラリが1つのファイルにマッシュアップされているときに誰かがHistory.jsを使用しようとしたかどうかを知りたいです。


3
追加すると、Firefoxの動作は仕様どおりの正しい動作です。Chrome 34以降、修正されました。Opera開発者のためのイェイ!
Henry Chan

回答:


49

バージョン19のGoogle Chromeでは、@ spliterのソリューションが機能しなくなりました。@johnnymireが指摘したように、Chrome 19のhistory.stateは存在しますが、nullです。

私の回避策は、window.historyに状態が存在するかどうかのチェックにwindow.history.state!== nullを追加することです。

var popped = ('state' in window.history && window.history.state !== null), initialURL = location.href;

すべての主要なブラウザとChromeバージョン19および18でテストしました。動作するようです。


5
ヘッドアップをありがとう; 私はあなたが単純化することができると思うinnullだけに確認してください!= nullそれはの世話をしますので、undefined同様に。
pimvdb

5
これはnull、のhistory.pushState()ようなメソッドで常に使用する場合にのみ機能しますhistory.pushState(null,null,'http//example.com/)。確かに、それはおそらくほとんどの使用法でしょう(それがjquery.pjax.jsや他のほとんどのデモで設定されている方法です)。しかし、ブラウザの実装している場合window.history.state(FFとChrome 19+など)、window.history.stateリフレッシュ上またはブラウザの再起動後に非nullになる可能性
unexplainedBacn

19
これはChrome 21では機能しなくなりました。ページの読み込み時にpopをfalseに設定し、プッシュまたはポップでpopをtrueに設定しただけです。これは、最初のロード時のChromeのポップを無効にします。それのより良いここで少し説明:github.com/defunkt/jquery-pjax/issues/143#issuecomment-6194330
チャド・フォン・なう

1
@ChadvonNauの素晴らしいアイデア、そしてそれは御馳走です-どうもありがとう!
sowasred2012 2013年

私はこれと同じ問題を抱えており、単純な@ChadvonNauソリューションの使用を終了しました。
Pherrymason 2013年

30

onpopstateに追加するハンドラーごとに特別な対策を講じたくない場合は、私のソリューションが興味深いかもしれません。このソリューションの大きな利点は、ページの読み込みが完了する前にonpopstateイベントを処理できることです。

onpopstateハンドラーを追加する前にこのコードを1回実行するだけで、すべてが期待どおりに機能するはずです(別名Mozilla ^^)

(function() {
    // There's nothing to do for older browsers ;)
    if (!window.addEventListener)
        return;
    var blockPopstateEvent = document.readyState!="complete";
    window.addEventListener("load", function() {
        // The timeout ensures that popstate-events will be unblocked right
        // after the load event occured, but not in the same event-loop cycle.
        setTimeout(function(){ blockPopstateEvent = false; }, 0);
    }, false);
    window.addEventListener("popstate", function(evt) {
        if (blockPopstateEvent && document.readyState=="complete") {
            evt.preventDefault();
            evt.stopImmediatePropagation();
        }
    }, false);
})();

使い方:

ドキュメントが読み込まれると、Chrome、Safari、およびおそらく他のWebkitブラウザがonpopstateイベントを発生させます。これは意図されたものではないため、ドキュメントが読み込まれた後の最初のイベントループcicleまでpopstateイベントをブロックします。これは、preventDefaultおよびstopImmediatePropagation呼び出しによって行われます(stopPropagation stopImmediatePropagationがすべてのイベントハンドラー呼び出しを即座に停止するのとは異なります)。

ただし、Chromeが誤ってonpopstateを起動した場合、ドキュメントのreadyStateはすでに「完了」になっているため、ドキュメントの読み込みが完了する前に起動されたopopstateイベントで、ドキュメントが読み込まれる前にonpopstateを呼び出すことができます。

アップデート2014-04-23:ページが読み込まれた後にスクリプトが実行されるとpopstateイベントがブロックされるバグを修正しました。


4
私にとってこれまでのところ最高の解決策。私は長年setTimeoutメソッドを使用してきましたが、ページの読み込みが遅いときやユーザーがpushstateを非常に速くトリガーしたときにバグのある動作が発生します。window.history.state状態が設定されている場合、リロード時に失敗します。pushStateの前に変数を設定すると、コードが煩雑になります。ソリューションは洗練されており、他のコードベースに影響を与えることなく、他のスクリプトの上にドロップできます。ありがとう。
2014年

4
これが解決策です!この問題を解決しようとするとき、私がこれまで数日前に読んだとしたら。これは完璧に機能した唯一のものです。ありがとうございました!
ベンフレミング

1
優れた説明と試してみる価値のある唯一の解決策。
Sunny R Gupta、2014年

これは素晴らしいソリューションです。執筆時点では、最新のクロムとFFは問題ありませんが、問題はサファリ(mac&iphone)にあります。このソリューションは魅力のように機能します。
Vin

1
わっ!Safariがあなたの1日を台無しにしている場合、これらはあなたが探しているコードです!
DanO、2015

28

コンテンツがロードされるまでにかかる時間を知らないため、タイムアウト後にpopstateイベントが発行される可能性があるため、setTimeoutのみを使用することは適切な解決策ではありません。

これが私の解決策です:https//gist.github.com/3551566

/*
* Necessary hack because WebKit fires a popstate event on document load
* https://code.google.com/p/chromium/issues/detail?id=63040
* https://bugs.webkit.org/process_bug.cgi
*/
window.addEventListener('load', function() {
  setTimeout(function() {
    window.addEventListener('popstate', function() {
      ...
    });
  }, 0);
});

これは私のために働いた唯一の解決策です。ありがとう!
ジャスティン

3
甘い!最も賛成または受け入れられている回答よりもうまく機能します!
ProblemsOfSumit

誰かがこのソリューションへのアクティブなリンクを持っていますか?私は与えられたリンクに到達できませんgist.github.com/3551566
Harbir

1
なぜ要点が必要なのですか?
Oleg Vaskevich 14

完璧な答え。ありがとう。
シデロキシロン

25

解決策はjquery.pjax.jsの行195-225にあります。

// Used to detect initial (useless) popstate.
// If history.state exists, assume browser isn't going to fire initial popstate.
var popped = ('state' in window.history), initialURL = location.href


// popstate handler takes care of the back and forward buttons
//
// You probably shouldn't use pjax on pages with other pushState
// stuff yet.
$(window).bind('popstate', function(event){
  // Ignore inital popstate that some browsers fire on page load
  var initialPop = !popped && location.href == initialURL
  popped = true
  if ( initialPop ) return

  var state = event.state

  if ( state && state.pjax ) {
    var container = state.pjax
    if ( $(container+'').length )
      $.pjax({
        url: state.url || location.href,
        fragment: state.fragment,
        container: container,
        push: false,
        timeout: state.timeout
      })
    else
      window.location = location.href
  }
})

8
このソリューションは、Windows(Chrome 19)では動作しなくなりましたが、Mac(Chrome 18)では動作しました。history.stateはChrome 19に存在し、18には存在しないようです
。– johnnymire

1
@johnnymire Chrome 19より下のソリューションを投稿しました:stackoverflow.com/a/10651028/291500
Pavel Linkesch '18

誰かがこの回答を編集して、このChrome 19以降の動作を反映する必要があります。
ホルヘスアレスデリス2013

5
解決策を探している人にとって、Torbenからの答えは、現在のChromeおよびFirefoxバージョンの魅力のように機能します
JorgeSuárezde Lis 2013年

1
2015年4月、私はこのソリューションを使用してSafariの問題を解決しました。Cordovaを使用してハイブリッドiOS / Androidアプリで戻るボタンを押す処理を実現するために、onpopstateイベントを使用する必要がありました。Safariは、プログラムで呼び出したリロードの後でも、onpopstateを起動します。私のコードの設定方法では、reload()が呼び出された後、無限ループに入りました。これはそれを修正しました。イベント宣言の後の最初の3行(および上部の割り当て)だけが必要でした。
Martavis P. 2015

14

pjaxを再実装するより直接的な解決策は、pushStateで変数を設定し、popStateで変数を確認することです。これにより、初期のpopStateがロード時に一貫して起動しません(jquery固有のソリューションではなく、イベントに使用するだけです)。

$(window).bind('popstate', function (ev){
  if (!window.history.ready && !ev.originalEvent.state)
    return; // workaround for popstate on load
});

// ... later ...

function doNavigation(nextPageId) {
  window.history.ready = true;

  history.pushState(state, null, 'content.php?id='+ nextPageId); 
  // ajax in content instead of loading server-side
}

常にfalseと評価されるとしたらどうでしょうか。つまり、window.history.ready常に本当です。
Andriy Drozdyuk、2012年

例をもう少し明確にするために更新しました。基本的に、すべてクリアした後、popstateイベントのみを処理したいだけです。ロード後500msにsetTimeoutを使用しても同じ効果を得ることができますが、正直なところ、少し安価です。
トムマッケンジー

3
これがIMOの答えになるはずです。Pavelsソリューションを試しましたが、Firefoxで正しく機能しませんでした
Porco

これは、この厄介な問題の簡単な解決策として私に役立ちました。どうもありがとう!
nirazul 2013

!window.history.ready && !ev.originalEvent.state- ページを更新しても、これら2つ定義されていません。したがって、コードは実行されません。
chovy

4

Webkitの最初のonpopstateイベントには状態が割り当てられていないため、これを使用して不要な動作をチェックできます。

window.onpopstate = function(e){
    if(e.state)
        //do something
};

元のページに戻ることができる包括的なソリューションは、このアイデアに基づいています。

<body onload="init()">
    <a href="page1" onclick="doClick(this); return false;">page 1</a>
    <a href="page2" onclick="doClick(this); return false;">page 2</a>
    <div id="content"></div>
</body>

<script>
function init(){
   openURL(window.location.href);
}
function doClick(e){
    if(window.history.pushState)
        openURL(e.getAttribute('href'), true);
    else
        window.open(e.getAttribute('href'), '_self');
}
function openURL(href, push){
    document.getElementById('content').innerHTML = href + ': ' + (push ? 'user' : 'browser'); 
    if(window.history.pushState){
        if(push)
            window.history.pushState({href: href}, 'your page title', href);
        else
            window.history.replaceState({href: href}, 'your page title', href);
    }
}
window.onpopstate = function(e){
    if(e.state)
        openURL(e.state.href);
};
</script>

これまだ2回起動する可能性がありますが(多少気の利いたナビゲーションで)、前のhrefをチェックするだけで処理できます。


1
ありがとう。iOS window.history.stateでもnullであるため、pjaxソリューションはiOS 5.0で機能しなくなりました。e.stateプロパティがnullかどうかを確認するだけで十分です。
ハスキー

このソリューションには既知の問題がありますか?私がテストしたどのブラウザでも完全に動作します。
ジェイコブユーイング

3

これが私の回避策です。

window.setTimeout(function() {
  window.addEventListener('popstate', function() {
    // ...
  });
}, 1000);

UbuntuのChromium 28では私には適していません。時々、イベントはまだ発生します。
ホルヘスアレスデリス2013

これを自分で試しましたが、常に機能するとは限りません。初期ページのロードによっては、ポップステートがタイムアウトよりも遅く起動する場合があります
Andrew Burgess

1

これが私の解決策です:

var _firstload = true;
$(function(){
    window.onpopstate = function(event){
        var state = event.state;

        if(_firstload && !state){ 
            _firstload = false; 
        }
        else if(state){
            _firstload = false;
            // you should pass state.some_data to another function here
            alert('state was changed! back/forward button was pressed!');
        }
        else{
            _firstload = false;
            // you should inform some function that the original state returned
            alert('you returned back to the original state (the home state)');
        }
    }
})   

0

Chromeがページの読み込み時にpopstateを起動しないようにする最善の方法は、https: //code.google.com/p/chromium/issues/detail?id = 63040に賛成投票することです。彼らは、ChromeがHTML5仕様に完全に準拠していないことを2年間認識しており、まだ修正されていません。


2
これは、クロム34以降で修正されました
matys84pl

0

event.state !== null最初にロードされたページに履歴に戻る使用の場合、非モバイルブラウザーでは機能しません。私はsessionStorageを使用して、ajaxナビゲーションが実際にいつ開始するかをマークします。

history.pushState(url, null, url);
sessionStorage.ajNavStarted = true;

window.addEventListener('popstate', function(e) {
    if (sessionStorage.ajNavStarted) {
        location.href = (e.state === null) ? location.href : e.state;
    }
}, false);


window.onload = function(){if(history.state === null){history.replaceState(location.pathname + location.search + location.hash、null、location.pathname + location.search + location.hash); }}; window.addEventListener( 'popstate'、function(e){if(e.state!== null){location.href = e.state;}}、false);
イリュージョニスト2016

-1

提示された解決策は、ページの再読み込みに問題があります。以下の方がうまくいくようですが、私はFirefoxとChromeのみをテストしました。それは違いがあるように思われること、実際に使用していますe.event.statewindow.history.state

window.addEvent('popstate', function(e) {
    if(e.event.state) {
        window.location.reload(); // Event code
    }
});

e.event定義されていません
chovy

-1

100万のブラウザの非互換性を解消するため、History.jsを実際に使用する必要があります。私は手動で修正ルートをたどっただけで、後で道を見つけるだけで見つけることができる問題がどんどん見つかりました。最近はそれほど難しくありません。

<script src="//cdnjs.cloudflare.com/ajax/libs/history.js/1.8/native.history.min.js" type="text/javascript"></script>

そして、https://github.com/browserstate/history.jsで APIを読んでください


-1

これで問題は解決しました。私がしたことは、ページロードで発生するpopstateイベントを見逃すのに十分な時間、関数の実行を遅らせるタイムアウト関数を設定することだけでした

if (history && history.pushState) {
  setTimeout(function(){
    $(window).bind("popstate", function() {
      $.getScript(location.href);
    });
  },3000);
}

-2

イベントを作成し、onloadハンドラーの後に発生させることができます。

var evt = document.createEvent("PopStateEvent");
evt.initPopStateEvent("popstate", false, false, { .. state object  ..});
window.dispatchEvent(evt);

これは、Chrome / Safariではわずかに機能しなくなりましたが、パッチをWebKitに送信したところ、まもなく入手可能になるはずですが、「最も正しい」方法です。


-2

これはFirefoxとChromeで私のために働いた

window.onpopstate = function(event) { //back button click
    console.log("onpopstate");
    if (event.state) {
        window.location.reload();
    }
};
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.