タイトルを付けるのが難しかった…未来の自分が見返したときにこれでピンとくるのだろうか?
例えば、下記のようなケースで同一ページでもユーザからするとページ遷移したかのように感じるということがあります。
- Ajax通信でデータをフェッチし、既存のデータをリプレイスする形で画面に反映させる
- サマリーページと詳細ページを同一HTML内で返却していて、JSによって表示を切り替えている
このような場合に、何もしなければ、ページ内でのまるで遷移したかのような行動は反映されていないため、「戻ると想定していたページと違う」ことが予想されます。この行動をブラウザの履歴に反映させようというやつです。
JSのHistory APIを使う
実質、下記を見れば終わり!
どうしてMDNのサイトってこんなに見やすいのだろうか?神か?ありがとうございます!
pushstate
で履歴を挿入する
履歴エントリ群はスタックのデータ構造を持っている。history.pushState()
を使うことで、そのスタックに履歴エントリを追加することができる。
最初に出した例で言うと、Ajax通信が正常終了したときにそのAjax通信を再現できる情報を詰めて突っ込めば良さそう。キャッシュ可能なデータならば、Ajax通信のレスポンスヘッダにCache-Control
ヘッダ付けて、private
かつmax-age: 120
にでもしておけば、2分以内にブラウザバックを押したときに再度通信が走ることはないので快適かもしれません。
let stateObj = { page: 2, }; let title = `${document.title} - 2` /** * JSDocの使い方間違っているけど、分かりやすいので… * @param {Object} stateObj エントリと一緒に保存できるオブジェクト(空でもOK) * @param {string} title 文書のタイトル(Safari 以外のブラウザはこのパラメータを無視) * @param {string} URL 絶対パス相対パス問わず */ history.pushState(stateObj, title, "/product?page=2");
popstate
をフックする
サマリーページと詳細ページを同一HTML内で返却していて、JSによって表示を切り替えている
最初に挙げたこのケースの場合には、history.pushState()
を使わなくとも、history.popState
へのhookとURLの#(ハッシュ)
を使えば実現可能。
- 詳細への遷移の際に、URLの
#(ハッシュ)
にどの要素を展開しているかを残しておく- https://example.com/product#detail-1
- 擬似的な遷移を実現している関数内で
location.hash=detail-1
- 代わりに
pushState()
を使っても良さげ
- ユーザがブラウザバックをしたときに発生するイベント
popstate
をフックして処理を行う
// pureJS window.onpopstate = function() { let currentState = history.state; // 現在の履歴のstateを読み込む } // jQuery $(window).on('popState', function () { if (location.hash != undefined) { $(location.hash).css('display', 'block'); $('#summmary').css('display', 'none'); } })
感想
SPAのページ遷移もHistory APIを使って実現しているのかなと思いました。
windowオブジェクトにぶら下がっているオブジェクトを全然把握できていなくて、JSで実現できることは自分の想像以上に多くあるんだろうなとも感じますね。