Ajax通信でブラウザの「戻る」ボタンを使う

Ajax通信は便利だが、厄介な問題を抱えている。
ブラウザの「戻る」ボタンが使えないのだ。
Historyが残らないのである。
これはかなりマズイ。
例えば割と時間をかけて取得した画面を表示した後、一旦別の画面に遷移したが、また直前の(時間をかけて取得した)画面へ戻りたい時、ブラウザの「戻る」で直前の履歴に戻れなければ、もう一度時間をかけて画面の情報を取得しなければならない。
これではユーザから必ずクレームが来る。
ところがjavascriptにこれを解決する手段が無いのだ。
(ハック的な実現方法はあるらしいが、使わないほうが無難だ。)
途方に暮れていると、html5でまさにこれを解決する手段が提供されていた。
history.pushStateである。
早速使ってみよう。

その前にこの関数のcallingシーケンスは以下だ。

history.pushState(stateObj, title, url);
stateObj: 履歴エントリに関連付けられるJavaScriptオブジェクト
title: 将来的拡張(現在未使用です。)
url: 履歴エントリのURL

以下はサンプルだ。

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <script src="./jquery-1.7.2.min.js"></script>
  <title>javascript test</title>
  <script type="text/javascript">
    var doajax = function(id, url) {
      document.body.style.cursor = 'wait';
      $.ajax({
        type: "get",
        url: url,
        dataType: "text",
        success: function(res){
          $(id).html(res);
          if(window.history && window.history.pushState) {
            history.pushState({response: res, id: id}, null);
          }
        },
        complete: function(xhr, textStatus) {
          document.body.style.cursor = 'auto';
        },
        error: function(xhr, textStatus, error) {
          console.log('error...: ' + textStatus + ' :' + error);
        }
      });
    }
    window.addEventListener(
      "popstate",
      function(event) {
        if(event.state != null) {
          $(event.state.id).html(event.state.response);
        } else {
          $("#target").html("hello, history");
        }
      }
    );
  </script>
</head>
<body>
  <div id="target">hello, history</div>
  <input type="button" value="go 1!" onClick="doajax('#target', 'http://localhost/1.txt')"><br />
  <input type="button" value="go 2!" onClick="doajax('#target', 'http://localhost/2.txt')"><br />
  <input type="button" value="go 3!" onClick="doajax('#target', 'http://localhost/3.txt')"><br />
</body>
</html>

DIV要素に表示された「hello, history」を「go 1!」、「go 2!」、「go 3!」ボタンで書き換える。
書き換えはajaxで行う。
取得するデータは単なるtextファイルだ。
local環境に1.txt、2.txt、3.txtを用意する。
ajaxのコールバックsuccessでpushStateを使う。

if(window.history && window.history.pushState) {
  history.pushState({response: res, id: id}, null);
}

最初のif文はpushStateの実装のチェックだ。
history.pushStateの第一引数にsuccessのレスポンス(1.txt or 2.txt or 3.txtの内容)と更新する要素のidを持つオブジェクトを指定する。第3引数は省略している。
(省略するとブラウザのURLは変わらない。指定すればこの内容がURLに表示される。)
このオブジェクトはaddEventListenerで登録する”popstate”コールバックで送られてくる。
“popstate”コールバックはブラウザの「戻る」「進む」ボタン押下時に呼び出される。
このタイミングでajaxのsuccessと同様の処理を行う。

window.addEventListener(
  "popstate",
  function(event) {
    if(event.state != null) {
      $(event.state.id).html(event.state.response);
    } else {
      $("#target").html("hello, history");
    }
  }
);

event.stateがnullでなければ履歴が存在するので、event.stateの内容(successのレスポンスと更新対象の要素ID)でDOMを更新する。event.stateがnullであれば履歴が無いので初期状態に戻す。
これで履歴が再現できる。

html5に感謝!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です