History API を取り扱う

HTML5 では pushState() および replaceState() メソッドが導入され、それぞれにより履歴エントリの追加と修正が可能となりました。 これらのメソッドは window.onpopstateイベントと連動して動作します。

履歴エントリの追加と修正

pushState() を使うことで、履歴の状態を変更した後に生成される XMLHttpRequest オブジェクトの HTTP ヘッダー中のリファラも変更されます。リファラは XMLHttpRequest オブジェクトが生成された時点での this となるウィンドウの持つドキュメントの URL となります。

pushState() の例

以下の JavaScript は http://mozilla.org/foo.html において実行されると仮定します:

let stateObj = {
    foo: "bar",
};

history.pushState(stateObj, "page 2", "bar.html");

これにより URL バーには http://mozilla.org/bar.html と表示されますが、ブラウザは bar.html をロードすることはなくまたその存在をチェックすることはありません。

ユーザが http://google.com に移動し、それから戻るボタンをクリックしたとします。このとき、URL バーは http://mozilla.org/bar.html を表示し、history.state は stateObj になります。popstate イベントはページがリロードされたために発火しません。ページそれ自体は bar.html のように見えます。

再び戻るボタンをクリックすると、URL は http://mozilla.org/foo.html へ変化し、 popstate イベントが発火され、state は null オブジェクトとなります。ここでもまた、popstate イベントを受け取って手動でコンテンツを変更することは可能ですが、戻るという操作によって戻る前のコンテンツが変更されるわけではありません。

Note: Although history.back() normally behaves the same way as clicking the Back button, the history.back() method acts differently from the browser Back button after using history.pushState(). Calling history.back() after history.pushState() does not raise a popstate event while clicking on the browser's Back button does raise the event.

pushState() メソッド

pushState() は、state オブジェクト、タイトル(現在のところ無視されます)、そしてURL(任意)の3つのパラメータを使用します。これら3つのパラメータについて詳細に見ていきましょう:

  • state オブジェクト — state オブジェクトは pushState() によって作成される新しい履歴エントリに関連付けられる JavaScript オブジェクトです。ユーザーが新しいエントリに移動すればいつでも、popstate イベントが発火して、履歴エントリの state オブジェクトのコピーがイベントの state プロパティへと含まれることとなります。

    state オブジェクトは何であってもシリアライズされます。Firefox はユーザーのディスクに state オブジェクトを保存し、ユーザーがブラウザを再起動した際に state オブジェクトを復元するため、シリアライズされた状態での state オブジェクトの最大文字数は640000文字と、サイズの制限がされています。シリアライズ後にこの最大文字数を上回ることになる state オブジェクトを pushState() に渡した場合、pushState()は例外を投げます。これを上回るスペースが必要な場合、sessionStorage または localStorage の使用を推奨します。

  • タイトルSafari 以外のブラウザはこのパラメータを無視します。しかし将来的には使えるようになるかもしれません。将来的な変更に備えて、空の文字列を渡しておくべきでしょう。あるいは、移動しようとしている先の状態を示す短いタイトルを渡しておくこともできます。

  • URL — このパラメーターは、新しい履歴エントリの URL が指定します。pushState() の呼び出しの後、ブラウザはこの URL のロードを行わないと注記しておきますが、ユーザーがブラウザを再起動させた後などでは、新しく指定された URL をロードすることがあります。新しい URL は絶対パスである必要は無く、相対パスであった場合は、現在のURLとの相対関係が解決されます。新しい URL は現在の URLと same origin でなければなりません。でなければ pushState() は例外を発生させるでしょう。このパラメータは任意であり、指定されなかった場合は現在のドキュメントの URL が設定されます。

注: Gecko 2.0 (Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1) より Gecko 5.0 (Firefox 5.0 / Thunderbird 5.0 / SeaMonkey 2.2) に於いては、渡されたオブジェクトは JSON を使用してシリアライズされます。Gecko 6.0 (Firefox 6.0 / Thunderbird 6.0 / SeaMonkey 2.3) より、オブジェクトは the structured clone algorithm を使用してシリアライズされます。これにより多種多様なオブジェクトを安全に渡せるようになります。

ある意味では、pushState() の呼び出しは window.location = "#foo"; と設定するのと似ています。どちらも、現在のドキュメントに関連する別の履歴エントリの生成とアクティベートを行います。ですが pushState() にはいくらかの利点があります:

  • 新しい URL は、現在の URL と同じドメインであればどの URL にもなることができます。対照的に、window.location では hash の変更しかできず、同じ document のままとなります。
  • 必ずしも URL を変更する必要はありません。対照的に、window.location = "#foo"; では、現在の hash が #foo でない場合、新しい履歴エントリの作成以外のことはできません。
  • 新しい履歴エントリに任意のデータを関連付けることができます。hash を基にしたアプローチでは、関連するデータを含めた短い文字列を全てエンコードする必要があります。
  • If title is subsequently used by browsers, this data can be utilized (independent of, say, the hash).

新しい URLが、変更前のURLから hash のみを変更した URL である場合であっても、 pushState()hashchange イベントを発火させることはないと注記しておきます。

In a XUL document, it creates the specified XUL element.

In other documents, it creates an element with a null namespace URI.

replaceState() メソッド

history.replaceState() は丁度 history.pushState() のように動作しますが、pushState() と異なる点として、 replaceState() は新しく履歴エントリを作成する代わりに現在の履歴エントリを修正します。Note that this doesn't prevent the creation of a new entry in the global browser history.

具体的には、何らかのユーザーのアクションを受け、現在の履歴エントリの URL または state オブジェクトを更新したい場合に replaceState() が役立ちます。

注: Gecko 2.0 (Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1) より Gecko 5.0 (Firefox 5.0 / Thunderbird 5.0 / SeaMonkey 2.2) に於いては、渡されたオブジェクトは JSON を使用してシリアライズされます。Gecko 6.0 (Firefox 6.0 / Thunderbird 6.0 / SeaMonkey 2.3) より、オブジェクトは the structured clone algorithm を使用してシリアライズされます。これにより多種多様なオブジェクトを安全に渡せるようになります。

replaceState() の例

Suppose http://mozilla.org/foo.html executes the following JavaScript:

var stateObj = { foo: "bar" };
history.pushState(stateObj, "page 2", "bar.html");

The explanation of these two lines above can be found at "Example of pushState() method" section. Then suppose http://mozilla.org/bar.html executes the following JavaScript:

history.replaceState(stateObj, "page 3", "bar2.html");

This will cause the URL bar to display http://mozilla.org/bar2.html, but won't cause the browser to load bar2.html or even check that bar2.html exists.

Suppose now that the user navigates to http://www.microsoft.com, then clicks the Back button. At this point, the URL bar will display http://mozilla.org/bar2.html. If the user now clicks Back again, the URL bar will display http://mozilla.org/foo.html, and totally bypass bar.html.

popstate イベント

アクティブな履歴エントリが変更される度にウィンドウへと popstate イベントが発行されます。pushState の呼び出しまたは replaceState の呼び出しの影響によって、アクティベートされた履歴エントリが作成された場合、popstate イベントの state プロパティは履歴エントリの state オブジェクトのコピーを含みます。

使い方のサンプルは window.onpopstate を参照してください。

現在の状態を読み取る

ページが読み込まれたとき、 ページは null ではない state オブジェクトを持っているかもしれません。これは例えば、(pushState() または replaceState()) の使用によって)ページに state オブジェクトが設定されており、ユーザーがブラウザを再起動した場合に起こりえます。ページを再読み込みした際にページは onload イベントを受け取りますが popstate イベントは受け取られません。しかしながら history.state プロパティを読み取った場合、popstate が発火した際に取得できるであろう state オブジェクトを得ることができるでしょう。

このように history.state プロパティを用いることで、 popstate イベントを待つことなく現在の履歴エントリの state を読み取ることができます:

let currentState = history.state;