2011年9月15日

History API を使ってみる

History API を使ってみます。 正確には、History API をクロスブラウザ対応でラップしてある history.js を使ってみます。

history.jsjquery-history を再実装したもので、 jQuery のプラグインとしてしか使えなかったものを汎用的に再実装し、 各種ライブラリへのアダプタを用意したものです。 アダプタは YUI や Prototype などにも対応しているらしいので、 jQuery 以外の JavaScript ライブラリと組み合わせることもできます。

なお、ブラウザの履歴に関する操作は Mozilla のサイトで網羅的に解説されています。

ライブラリの読み込み

ブラウザの履歴 (window.history) には back()go() などの API がありましたが、 いわゆるモダンブラウザには pushState()replaceState() という API が追加されています。 このため、ターゲットとするブラウザによって読み込むべき JavaScript ファイルが異なります。 ごちゃごちゃ考えるのが面倒な場合にはすべてを読み込めば動きますが、 HTML5 に関することを HTML4 までの世界に適用させるべきかは一考の価値がありそうです。

ともあれ、jQuery をベースにしてザクッと全てを読み込む場合は次のような感じでしょうか。 $STATIC_URL は適当に調整するか、何らかのビルドスクリプトで置換します。

  <script src="//ajax.googleapis.com/ajax/libs/jquery/1.6/jquery.js"></script>
  <script>window.jQuery || document.write('<script src="$STATIC_URL/js/jquery.min.js">\x3C/script>')</script>
  <script src="//ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"></script>
  <script>window.JSON || document.write('<script src="$STATIC_URL/js/json2.min.js">\x3C/script>')</script>
  <script src="$STATIC_URL/js/amplify.store.min.js"></script>
  <script src="$STATIC_URL/js/history.adapter.jquery.min.js"></script>
  <script src="$STATIC_URL/js/history.min.js"></script>
  <script src="$STATIC_URL/js/history.html4.min.js"></script>

なお、 jquery.tmpl.min.js は他の用途で使っているものなので、History API とは関係ありません。 また、各種ファイルに .min を付けているのは個人的な管理を簡単化するためです。

履歴の変化を追いかける

ライブラリを読み込んだら、最初のコードを書きます。 これは history.js の README の最初の方にあるものをコピペすれば動くはずです。 とりあえずこんな感じ。

$(function() {

    var History = window.History; // 小文字ではなく大文字の H を使います。
    if (!History.enabled) {
         // History.js が無効になっています。
         // HTML4 のブラウザをサポートするかどうかによります。
        return false;
    }

    // StateChange イベントを紐付けます。
    History.Adapter.bind(window, 'statechange', function() { // popstate ではなく statechange を使います。
        var State = History.getState(); // event.state ではなく History.getState() を使います。
        History.log(State.data, State.title, State.url);
    });

    // 状態を遷移させます。
    // 引数は順番に「状態オブジェクト」「タイトル」「URL」です。
    History.pushState({state:1}, "State 1", "?state=1");
    History.pushState({state:2}, "State 2", "?state=2");
    History.replaceState({state:3}, "State 3", "?state=3");
    History.pushState(null, null, "?state=4");
    History.back();
    History.back();
    History.back();
    History.go(2);

});

「状態オブジェクト」って何?という感じですが、 履歴のある時点に関連付く JavaScript オブジェクト (シリアライズ可能なもの) なら何でも良さそうです。 とはいえデータサイズには上限があり、Firefox の場合は 640k だそうです。 これを超過した場合は例外が投げられますので、大きなデータを保存しておくためには sessionStoragesessionStorage を使いましょう、とのこと。

さて、HTML ファイルを開いて Firebug などの JavaScript コンソールを開いてみると次のようになります。 ブラウザのアドレスバーも変わっているはずです。

終わりに

history.js を使ってブラウザの履歴を管理しました。 サーバーサイドから JSON を送るだけにしてしまうと「ページ」という概念が希薄になりがちですので 「戻る」「進む」が思い通りにいかないことが多々あります。 その解決策として pushState() はとても良い方法だと思います。

サーバーサイドで HTML を生成する場合には HTTP の拡張ヘッダーを組み合わせて使う応用もあります。 jQuery Mobile でも使われるらしいので、ちょっと知っておくと良さそうです。

自分で Web アプリケーションを作る、という視点では PJAX の方が良いかもしれませんが、 Solr などの JSON ライターが存在するアプリケーションと組み合わせる場合には window.history を管理する方法は有用そうですね。

おまけ

history.js にはアダプタが登場しましたが、 JavaScript におけるデザインパターンや実装パターンは英語の記事だとよくまとまっています (日本語の記事は特に調べていませんが)。 サーバーサイド JavaScript でも基本的なパターンは押さえておくべきなので、 何らかのプログラムを書くなら一読しておくべきなのかな、と。

また、多様なパターンを覚えておくと非同期処理もすっきり記述できるかもしれませんね。

コメントを投稿