githubのURLをうまく扱うオシャレなアレ = pjax
githubでは、たとえばファイルリストからファイルをクリックすると、なめらかに横にスライドしてファイルリストのビューからファイルの中身のビューに遷移するような、今時のwebappとしては当たり前のようなオシャレなことをしているのだが、よく見るとURL自体も書き換わっていて、ファイルリストのURLからファイルを示すURLに変わっている。これはいいな、と思っていたのだが、こういうことをpjaxと言うのだと教えてもらった。
よくあるのはURLのfragment (#のあとの部分)を書き換えておく方法。ここはwindow.location.hashでJavaScriptから参照できるから、ページがロードされたらそこを読み取って描画を変える。難点はJavaScriptが動かないとダメだということで、そういうブラウザやwget/curlのようなツールとの相性が悪い。というよりそれ以上に深刻なのは、ソーシャルブックマークやFacebookやGoogle Buzzでページの紹介をするのとも相性が悪いところかも。こういうツールでは、サーバ側でそのページを取りに行って、ページのタイトルだとか、本文の一部だとか、画像だとかを提示してくれるわけだが、JavaScriptを実行して頑張るようなのはなかなか難しい。
だがfragmentではなくてパスそのものが変わっているなら、こういう問題は引き起こされないはずってわけだ。かっこいい! しかし、どうやってURLを変えつつページの一部だけ書き換えるようなことができるのか。window.location.href=”new URL”みたいなやり方だと、ブラウザが勝手に新しいページのロードを始めてしまう。
全く知らなかったが、HTML5では、window.historyにいくつかの拡張がされていて、pjaxはそれを使ってみたこういうのを実現する。キーはpushStateというメソッド。 window.history.pushState(data, title, url) は、ブラウザ履歴にそのURLを追加する。titleはタイトル、それと後で使う用のdataも指定できる。追加するんじゃなくて最新のを書き換えるようにreplaceStateというのもある。ちなみに、セキュリティ上の問題から、pushStateなどで足せるURLはもとのページと同じoriginでなければならない。
pjaxというのは、第一義にはjQueryのプラグインの名前だが、つまるところ、pushStateをうまく使ってajaxなwebappっぽい動作を実現するためのやり方だ。基本的な概略としてはこういう感じ:
- 各アンカーにonclickイベントを仕込む
- onclickはそのアンカーのhrefを見てxhrを発行する。この時、X-PJAX: trueというヘッダフィールドを勝手に足す。これはサーバ側に、今のリクエストがpjaxのためのものなのか通常のものなのかを区別するためのもので、やり方はなんでもいい。リクエストパラメータを渡す方法もあるようだ。
- サーバ側は、リクエストがpjaxの場合はページの一部書き換えに必要なだけのデータを返す。そうでなければ普通のページコンテンツをそのまま返す。
- xhrのハンドラで正常にリクエストが受け取れたら、ページの一部分を書き換えるようのハンドラを呼ぶなどする。成功したらwindow.history.pushStateして完了。
こうして書きだしてみると、pushState以外は割と普通ですね……。URLをコピペなどした場合にはX-PJAXフィールドはないから、普通にページがロードされ、所定の役割は果たせるというわけだ。
なお、githubなどではブラウザの戻るボタンなどの場合でも逆方向スライドしたりオシャレにページ遷移している。pushStateした状態に履歴が移動するときはpopstateイベントが発生するので、こいつをとらえて同じようにハンドルすればそういうこともできるわけだ。もちろん実際には、失敗したらwindow.location.hrefを書き換えることで新しいページにちゃんと遷移させるなど、ちゃんとやるのがそれなりにめんどい部分も結構ありそうだ(なお、pushStateで渡したdataはpopstateのときに取り戻せる)。
pjaxのpがなんなのかはよくわからないが、pushStateのpなのかなあ。もはやjaxの部分はなんだかよくわからない……。ネーミングセンスに疑問はあるが(人のことは言えないが)、いずれにせよ「githubのオシャレなアレ」のことをpjaxと呼ぶのだ、といったん決めてしまえば、これは意思疎通に便利だなとは思った。
なるほど。
twitterの新URLが # を間に挟んでいるのが何故だろうと思っていましたが
もそういう理由なら納得できます。
twitterのあれはhashbangとかshebangとか呼ばれていて、元々は http://code.google.com/intl/ja/web/ajaxcrawling/docs/getting-started.html で提案されているものだったかと思いましたが、残念ながら不評です (しかも twitter がまたこれと微妙に違うという)