term タグ別の記事一覧

WebブラウザのHistory APIの挙動を知ろう

仕事でWebブラウザのHistory APIに関する質問を受けたのですが、意外と分かりにくいのでここでまとめておきます。

なお、History APIに関する大体のことはMDNのHistory API を取り扱うというドキュメントにまとめられていますので、こちらも参照しましょう。

履歴とは

Webブラウザ上では、閲覧履歴はスタック(本記事では「履歴スタック」と呼ぶ)として管理されます。履歴スタックはWebブラウザのウィンドウやタブごとに用意されています。ページの進む/戻る操作ではこの履歴スタック自体は変更されず、履歴スタック上の現在位置のみが変更されます。

Aタグなどであるページから別のページに遷移した場合、履歴スタック上の現在位置よりも先にある履歴項目が削除され、続いて履歴スタックの先頭ににそのURLを含む新たな履歴項目が追加されます。

履歴スタックのイメージ

History APIとは

History APIは、履歴スタックを操作するインターフェイスです(MDNのドキュメント)。履歴上で前に閲覧したページに戻ったり、戻った後に再度元のページに戻る、といった操作が可能です。ただし、Webブラウザの閲覧履歴はプライバシに関わるものとされるため、閲覧履歴のURLやタイトルなどへのアクセスはできません。

Webブラウザ上では、window.historyがHistoryインターフェイスを実装したオブジェクトになっています。

History APIの使用例1:前のページに戻る/次のページに進む

History APIでよく使われ、かつ理解しやすい使用例として、前のページに戻ったり、次のページに進む、というものがあります。これは、history.back()およびhistory.forward()メソッドで実行できます。

history.back()は、Webブラウザの「前に戻る」ボタンを押した場合と同じ挙動を実行します。また、history.forward()は「次に進む」ボタンを押した場合と同じ挙動を実行します。

「2つ前に戻る」「3つ先に進む」といった操作を行えるhistory.go()メソッドも用意されています。先に進む場合は正の数、前に戻る場合は負の数でいくつ進む/戻るかを指定します。たとえばhistory.go(1)history.forward()と、history.go(-1)history.back()と同じです。引数が指定されていない場合は、現在のページをリロードします。

history.lengthは履歴スタック上にいくつの項目(履歴)が存在しているかを示す値です。ただし、現在表示されているページが履歴スタック上のどの位置にあるかを知ることはできません。そのため、あまりこのプロパティのユースケースはないと思います。

History APIの使用例2:ブラウザのURLバーに表示されているURLを操作する

History APIでは、履歴だけでなくWebブラウザのURLバーに現在表示されているURLの操作を行うAPIも提供されています。history.replaceState()がそのメソッドです。

たとえば、URLバーに表示されているURLを「<現在表示しているWebページのドメイン>/foo/bar」に書き換えるには、次のように実行します。

window.history.replaceState(null, "", "/foo/bar");

クエリパラメータやハッシュを指定することも可能です。たとえば次のように実行すると、URLバーの表示内容は「<現在表示しているWebページのドメイン>/foo/bar?hoge=1#baz」になります。

window.history.replaceState(null, "", "/foo/bar?hoge=1#baz")

こういった操作は、JavaScriptを使って動的にページのコンテンツを変更するようなWebサイトで、現在の状態に特定のURLでアクセスさせたい(いわゆるパーマリンク、Permalinkを生成したい)場合に利用できます。

History APIの使用例3:新たな履歴を生成することなしにページ遷移を行う

さて、このhistory.replaceState()メソッドを実行すると、URLバーの表示内容が変わり、またwindow.location.hrefの値も指定したURLに変化します。一方でページの読み込みは行われず、またhistory.lengthの値も変化しません。これを利用し、ページをリロードするhistory.go(0)と組み合わせることで、新たな履歴を生成することなしにページ遷移を発生させることができます。

window.history.replaceState(null, "", "<遷移したいパス>");
window.history.go(0);

この場合、たとえば実行前のhistory.lengthが1なら、実行後もhistory.lengthは1のままです。

History APIの使用例4:ページ遷移なしに新たな履歴を生成する

History APIでは、逆にページ遷移なしに新たな履歴を生成することもできます。そのためのメソッドが、history.pushState()です。

たとえば次のように実行すると、履歴スタックの先頭に「<現在指定しているWebページのドメイン>/hoge」というURLが追加されます。

window.history.pushState(null, "", "/hoge");

このとき、URLバーの表示内容は「<現在指定しているWebページのドメイン>/hoge」になります。window.location.hrefもこの値になります。pushState()実行前に表示されているページが履歴スタック上の先頭であれば、history.lengthは1増えます。ただし、実際のページ遷移(ページの読み込み処理)は実行されません。

この状態でブラウザの戻るボタンを押したり、history.back()もしくはhistory.go(-1)を実行すると、URLバーの表示内容やwindow.location.hrefの内容はpushState()実行前のものに戻ります。ただし、その場合も実際のページ遷移(ページの再読み込み)は発生しません。

History APIの使用例5:実際にページ遷移が発生しない戻る/進む操作を検出する

前項で説明したように、history.pushState()で履歴を生成した後に戻るボタンを押したりhistory.back()もしくはhistory.go(-1)を実行すると、履歴スタック上の現在位置は変化しますが、ページ遷移は発生しません。こういったページ遷移なしの戻る/進む操作は、windowpopstateイベントで検出できます。

たとえば次のようにpopstateイベントに対するイベントハンドラを設定しておくと、ページ遷移なしの戻る/進む操作が発生した際にConsoleにそのイベント内容が出力されます。

window.addEventListener("popstate", (e) => console.log(e));

History APIの使用例6:履歴スタック上の履歴項目に任意の情報を紐付ける

history.replaceState()history.pushState()では、第1引数としてnullや任意のオブジェクトを与えることができます。このオブジェクトは、replaceState()の場合履歴スタック上の現在位置にある履歴項目内に、pushState()の場合は新しく生成された履歴項目内に格納されます。

履歴スタック上の現在位置にある履歴項目内に格納されたオブジェクトは、history.stateプロパティとして参照できます。

たとえば、あるページを表示している状態で、次のようにhistory.replaceState()stateオブジェクトとして{foo: 1}を設定します。

window.history.replaceState({foo: 1}, "");

この状態でhistory.stateを確認すると、指定したオブジェクトが格納されていることが分かります。

> history.state
{foo: 1}

その後、Aタグなどで別ページに遷移すると、history.stateの値は別のものに変わります。

> history.state
null

別ページへの遷移後、戻るボタンやhistory.back()などで元のページに戻ると、history.stateの値はそのページに紐づけたものに戻ります。

> history.state
{foo: 1}

なお、このhistory.stateは前述のpopstateイベントのイベントハンドラに渡されるPopStateEventstateプロパティとしても参照できます。

備考:履歴スタックが更新される条件

原則として、History APIを使用せずにページのURL(window.location.href)が変更されると、必ず履歴スタックは更新されます。たとえば<a href="#hoge">のようなハッシュのみを指定したAタグをクリックした場合、ページのロードは発生しませんが、履歴スタックは更新され、もし現在の履歴スタック上の位置が先頭だった場合、新たな履歴がスタックに追加され、history.lengthは1増えます。

x86_64版CentOS 5.5で64ビット版FirefoxでJavaプラグインを使う

 x86_64版CentOS 5.5のFirefoxでのJavaプラグイン導入に軽くハマったのでメモ。

 x86_64版のCentOS 5でFirefoxにJavaプラグインを導入したい場合、ググると「32ビット版Firefox+32ビット版Javaランタイムを使え」という話がぼちぼちとヒットするわけですが、とりあえずJavaプラグインだけを使いたい場合は64ビット版でも可能なようです。ていうか自分の場合、32ビット版を導入しようとしたらうまくいきませんでした……。

 ということで、手順。まず64ビット版Firefoxをインストール。

$ sudo yum install firefox  # 64ビット版Firefoxをインストール

 続いてjava.comのダウンロードページから「Linux x64 RPM」をダウンロード。

「Linux x86 RPM」をダウンロードする

「Linux x86 RPM」をダウンロードする

 RPMと書いてあるが、ダウンロードしたファイルはRPMではなくインストーラなので、root権限で実行してインストール。自動的にRPMがインストールされる。

$ chmod +x jre-6u21-linux-x64-rpm.bin
$ sudo ./jre-6u21-linux-x64-rpm.bin

 続いて設定。インストールしたjreを利用するようalternativesコマンドで指定。

$ sudo /usr/sbin/alternatives --install /usr/bin/java java /usr/java/jre1.6.0_21/bin/java 2
$ sudo /usr/sbin/alternatives --config java

 最後にmozilla用プラグインフォルダにプラグインのシンボリックリンクを張って完了。

$ cd /usr/lib64/mozilla/plugins/
$ sudo ln -sf usr/java/jre1.6.0_21/lib/amd64/libnpjp2.so .

 あとはFirefoxでabout:pluginsを開きプラグインが正しくインストールされているか確認したり、java.comにアクセスして動作を確認するなり適当にどうぞ。

激安・低サポートのVPS「prgmr.com」を借りる

 最近、月額490円からの「ServersMan@VPS」が話題だ。ほかにも980円からの「FC2 VPS」など、低価格のVPSはいくつかあるが、それ以前から存在する低価格のVPSサービスとして一部の間で知られていたのが「prgmr.com」である。

 prgmr.comはXenを使用したVPSで価格は$5/月からと低価格なのが魅力の1つだが、安さだけなら国内のVPSサービスと大きくは変わらない。ではなぜprgmr.comが注目されているのかというと、そのアレなたたずまいと、サポートの簡素さによるところが大きい。prgmr.comのトップページにはほかのVPSサービスのような画像による装飾は一切なく、アスキーアートで書かれた「prgmr.com」というバナーと「We don’t assume you are stupid.」という文言、そして料金やXenベースVPSの特徴が紹介されているのみ。一般人ならとりあえずこのサイトが何であるかも理解できないだろう、という作りである。

 このようにアレゲ感たっぷりのprgmr.comであるが、その一方でかなり自由度は高く、OSはDebian GNU/Linux、Ubuntu 10.04、CentOS 5.5が選択できるほか、AMD64アーキテクチャのXen上で動作するOSなら(自力で)任意のOSがインストールできる(可能性がある)、追加料金なしでグローバルIP付与と、独自のサーバー管理&Xenが分かる人にとってはかなり魅力的であったりする。

 ということで一部の人には魅力的なこのprgmr.com、ちょくちょく新規受付を開始しては定員に達して終了を繰り返しており、いつでも申し込みできるというわけではないのだが、ウォッチしていたらたまたま申し込み可能になっていたため、$12/月でメモリ512MiB、ディスク12GiB、月間ネットワーク転送量80GiBのプランを申しこんでみた。

Webでの申し込み後、メールでSSH公開鍵を送付。サーバー設定は必要十分

 Webで利用したいプランを選択し、オンラインフォームに必要事項を記入して申し込むと、下記のように利用したいディストリビューションとOpenSSH公開鍵を送信しろ、という旨のメールが来る。

you ordered a xen vps, 512MiB ram, 12GiB Disk 80 GiB transfer Xen VPS, $12/month username hylom

Before I can set you up, I need to know what distro you would like and an OpenSSH format public key (on a *NIX, run ssh-keygen and send me either the id_dsa.pub or id_rsa.pub file in an attachment)

 あとはこのメールの返信として公開鍵を添付し、Debian GNU/Linuxを使いたいという旨を伝えるだけだ。

Hi,

I want to use Debian GNU/Linux, and here’s my SSH public key.

Please check it.

 設定が完了すると、その旨がメールで連絡される。また、しばらくすると金払ってねメールがやってくる模様。料金の支払いはPayPal。

 続く。