DIARY :: AROUND THE CORNER :: 20120609001
「window.onpopstate」におけるChromeへの対応

DIARY :: AROUND THE CORNER :: 20120609001

登録させていただいている html5j.org さんの ML に、
Chrome において、「window.onpopstate」が最初のページ・ローディングの際にもコールされてしまう、
という問題への対応方法がないか?
という趣旨の 投稿 がありました。

いつもは ROM ばかりですが、この点については、私も貢献できるように思いましたので、エントリーとして書いてみました。ただ、この ML に登録していたメールアドレスが、公開用のものではなく、個人用のものであったのと、既に、別の方が解決方法(私の対応とは若干異なる方法)を投稿されており、問題解決となっていましたので、他の方にとっても有用な情報になればということで、当ブログにて公開することにしています。

私は、次のような方法で対応しています(自分が書いたコードを一部微修正して掲載しています(ほぼそのまま))。
window.onpopstate = myObj.popstateCallback;
var myObj = {
// Other definitions

popstateCallback:
function(event) {
if (!event.state) {
return;
}

// Your codes.
}
}
これは、Chrome において最初のページ・ローディングの際に「window.onpopstate」がコールされた時には、「event.state」自体が「null」である、という性質を利用しています。(*1)

上記 ML において投稿された別の解決方法においては「event」自体の存在確認をしていたので、それを踏まえると、条件判定の箇所は、以下のようにした方が良いかもしれません。
if (!event || !event.state) {
return;
}
もう一つ別の方法としては(こちらは、かなり以前に用いた方法なので、記憶がやや不確かですが)、コールバック・ファンクションを、最初のページ・ローディングの際に設定するのではなく、「Ajax のリクエストに対してサーバからレスポンスが返ってきた際に設定する」ということでも対応可能であったように思います。(*2)

お役に立ちましたらと思います。(*3)



*1:この対応方法は、当サイトにおいては、スマートフォン用のページの内部リンクを Ajax 化するために、 TNC App Site Module という Drupal 7 用のモジュールにて用いています。 

*2:この対応方法は、当サイトにおいては、通常の(PC 用の)ページの内部リンクを Ajax 化するために、 TNC Ajax Site Module という Drupal 7 用のモジュールにて用いています。 

*3stackoverflow にも、この問題について「Popstate on page's load in Chrome」と題する投稿がありました。 その投稿の中でも、当エントリー同様の解決方法が提示されていました。 しかしながら、そこでは、この方法は全く人気が無く、 支持を集めているのは、jQuery からヒントを得た次のような判定を行う解決方法でした。
var popped = ('state' in window.history && window.history.state !== null);
これは、「If history.state exists, assume browser isn't going to fire initial popstate.」という jQuery のコメントと、 「history.state in Chrome 19 exists, but it's null.」というユーザーからの情報に基づいたコードのようです。 使い方は、この部分のみに限ると、上記のコードとは別に、コールバック関数の冒頭に次のようなコードを追加しておきます。
if (!popped) {
popped = true;
return;
}
これによって、ページ・ローディングの際に、「window.history.state」を持たないブラウザー(〜 Chrome 18 等)と、 「window.history.state」の初期値が「null」であるブラウザー(Chrome 19 等)では、「popped」が「false」となり、
(1)ページ・ローディング(=初回のコール)の際には、上記の判定コードによって処理を終了する
(2)その時に「popped 」を「true」としておくことで、バックボタンのクリック等(=2回目以降のコール)では、処理を継続する
という挙動となります。 また、対象ブラウザ以外では、元々「popped 」は「true」であるため、上記の判定コードは常に処理を継続することになります。 とは言うものの、stackoverflow のこの投稿において、何故この解決方法が、 コードがシンプルであり余計な変数も不要である(それ故、プログラミング面でもデバイス負荷面でもより良いように思われる) 当エントリー同様の解決方法よりも人気があるのかという点については、私にはよく解りません。 もしかすると、Chrome をメインに使って開発しているプログラマーの方には、この方法がしっくり来るということなのでしょうか。
 ※2012年06月10日追記

TAG 1 ::
DIARY :: AROUND THE CORNER :: JavaScript
ARCHIVES 201206