Ajaxのサーバとクライアントを実装する際の基礎事項(ブラウザバック対応)を検討する

なお,このページの内容はIE(Internet Explorer 6)で検証したものです.他のブラウザでは動作が異なる可能性があるので注意してください.

ことの発端

Ajax化したBugTrackを動かしていて気づいたのだが,BugTrackの画面から別の画面に遷移して,そこからブラウザバックでまたBugTrackに戻ってくると,スクロールバーが一番下にあるわけでもないのに,先読み処理が実行されてしまう.

動きを細かく調べたところ,先読み処理の判断に使っているhidden値が,処理のタイミングで本来の値になっていないのが原因だった.

Alertを入れて調べたところ,ブラウザバックで戻ってきたときには,画面のhidden値は以下のように変化していた.

  1. body内に記述しているJavaScriptの実行時:hiddenは初期値 (HTMLに記述している値)
  2. 画面の描画が終わった後:hiddenには別画面に遷移する直前の値が復元される

そのため,一覧にどれだけ行を追加した状態であっても,ブラウザバックで戻ってきてJavaScriptの処理が走るときはまだ(1)の状態なので,初期のhidden値に合わせた処理が行われてしまい,最初と同じ処理を繰り返してしまうという話だった.

このあたりの内容について整理したので,以下に書きます...

ブラウザバック時の画面内容について

別のページに遷移してブラウザバックで戻ると,そのページが再描画される. このとき,直前まで表示していた内容がそのまま表示されるのが普通である. 確かにHTMLでページが構成されている場合はそのようになるが, しかしAjaxでinnerHTML属性にHTMLを注入して画面を構築している場合は,そうならない.

ajax_cache1.png ・・・ ページの初期状態

ajax_cache2.png ・・・ ページで色々操作すると,AjaxによるinnerHTML注入によって画面が変化する

ajax_cache3.png ・・・ その後,別のページに遷移してみる

そしてブラウザバックで戻ると,

ajax_cache4.png

ページの内容は初期状態に戻ってしまう.innerHTMLに注入した内容は失われる

これは閲覧者にとっては結構腹が立つので,何か対策が必要.

画面内容が最初に戻らないための対策

画面が最初の状態の戻るのを防ぐために,画面を構築するのに必要な情報を,hiddenに保持しておく.

hiddenに保持しておけば, ブラウザバックしたときには,直前に見ていたときの内容でhidden値は復元されるので, それを使って,innerHTMLの注入(画面の構築)をやり直せばよい.

まとめると,

  • 画面に対して何らかの動的な変化を加えるときには,それをhiddenに記録しておく
  • ページのbodyがonLoadされた後もしくはそれに準ずるタイミングで,記録しているhidden値を調べる.もし何か入っていれば,それを元にして画面の構築(innerHTMLの注入)を行う

という処理を行うことにした.

これによって,ブラウザバックで戻ってもページにはさっきまで出ていた内容が表示されている, という状態になるはず.

画面の構築をすべきタイミングの詳細

上で,
 ページのbodyがonLoadされた後もしくはそれに準ずるタイミング

と書いたが,要は,ある程度「遅い」タイミングである.

具体的にどう実装するかについて,以下で考える.

(a) body内で実行する (NG)

<body>
 ・・・
<script type="text/javascript">
 // ここでhiddenを参照して画面の構築をする
</script>
 ・・・
</body>
このタイミングでは,まだhidden値の復元が行われていない. そのため,直前に見ていたときの内容に画面を構築することはできない.初期状態に戻ってしまうので駄目.

(b) body.onLoadイベントで実行する (OK)

<body onLoad='reDraw()'>
 ・・・
<script type="text/javascript">
function reDraw() {
 // ここでhiddenを参照して画面の構築をする
}
</script>
 ・・・
</body>
このタイミングなら,hidden値の復元が行われている. そのため,直前に見ていたときの内容に画面を構築することができる.

(c) body内からタイマーを使って実行する (微妙)

<body>
 ・・・
<script type="text/javascript">
window.setTimeout("reDraw()", 1000); // 1秒後にreDraw()を呼び出す

function reDraw() {
 // ここでhiddenを参照して画面の構築をする
}
</script>
 ・・・
</body>
このタイミングなら,まずhidden値の復元は行われている. そのため,直前に見ていたときの内容に画面を構築することができる.

しかし,確信は持てない. setTimeout()での「1秒後」という設定には,何の根拠もない.

結局,(b)のbody.onLoad方式が理想だが,何らかの事情でonLoadイベントをカスタマイズできない場合は, 次善の策として(c)のタイマー方式を使うという感じ.

ただしタイマー方式は本当にあてにならないので,それでも支障がない場合にしか使えない. (といいつつ,カモランドでは使っています)

hidden以外に記録する案

上記のような微妙なタイミングの問題を回避するために,hidden以外に記録できないかと考えたが,以下のように駄目だった.

JavaScriptのグローバル変数に入れる案

JavaScriptのグローバル変数に入れると,ブラウザバックすると値が消えてしまった
→ 駄目

クッキーに入れる案

セッションクッキーに入れる方法を試したところ,クッキーに保存してもそれを読み込めないという現象が発生した.

色々試してみたところ,保存サイズが大きいと駄目だった.例えば以下のサイトによると,約4〜5KB程度までしか保存できないとのこと.

クッキーの最大サイズ制限について
http://www.teria.com/~koseki/memo/cookie/cookie_4k.html

ということで,カモランドの用途では,クッキー方式はサイズの制限が厳しくて使い物にならなかった.

kamolandをフォローしましょう


© 2021 KMIソフトウェア