Okuyama Yukihiko
A FRONT END ENGINEER FROM TOKYO, JAPAN.
HTML5から導入されたURLを操作するAPIの一つで、ブラウザの履歴を追加するためのメソッド
サーバーからページに必要なデータを非同期で取得し、インターフェイスの構築を行う技術の総称
PJAX使用前
https://sumally.com
https://sumally.com/p/1
HTML
JS
CSS
IMG
HTML
JS
CSS
IMG
PJAX使用後
https://sumally.com
ajaxでデータのみリクエスト
HTML
JS
CSS
IMG
HTML
pushStateで任意のURLに変更
// リンクがクリックされたらURLを変更
$('.js-pjaxLink').on('click', function (e) {
e.preventDefault();
var url = $(this).attr('href');
window.history.pushState('', '', url);
});
// コンテンツをレンダリング
var render = function (html) {
$('.js-pjaxContents').html(html);
};
// コンテンツをサーバーから非同期で取得
var loadContents = function (url) {
$.ajax({ url: url }).done(function (res) {
render(res.html);
});
};
// URLの変更を検知
$(window).on('popstate', function () {
loadContents(window.location.pathname);
});
// リンクがクリックされたらURLを変更
$('.js-pjaxLink').on('click', function (e) {
e.preventDefault();
var url = $(this).attr('href');
window.history.pushState('', '', url);
});
現在閲覧しているページのリンクを押下してしまうと、pushStateして履歴を追加しているのでブラウザの戻るボタンで戻ると同じページを延々と見ることになる。
かといってリンクをクリックさせないのもおかしい。
// リンクがクリックされたらURLを変更
$('.js-pjaxLink').on('click', function (e) {
e.preventDefault();
var url = $(this).attr('href');
var method = 'pushState';
if (isSamePath(url)) {
method = 'replaceState';
};
window.history[method]('', '', url);
});
現在閲覧しているページのリンクが押下された場合は、replaceStateを使用して現在の履歴を置き換える。
※実際はハッシュ(#tutorial)やクエリ(?object_id=11111111)も処理しないとダメなんでもっと複雑になる…。
// URLの変更を検知
$(window).on('popstate', function () {
loadContents(window.location.pathname);
});
ブラウザによってpopStateの挙動が違う。
Safariではonload時にpopstateイベントを発行するが、
その他ブラウザ(Chrome、FF、IE)はpopstateイベントを発行しない。
※昨年実装した時点では、Chromeはonload時にはpopstateイベントを発行しないが、
外部サイトに移動してからブラウザの戻るボタンで戻ってくるとpopstateが発行されていた。
// URLの変更を検知
$(window).on('popstate', function () {
if (isInitialLoad && isSafari) {
isInitialLoad = false;
return;
}
loadContents(window.location.pathname);
});
Safariで初回閲覧時はpopstateイベントを無視するようにする。
※e.originalEvent.stateでpushStateしたかどうかを調べて、pushStateされていなければ実行しないという判定方法もあるが、ブラウザの戻るボタン対策で再度サーバーからデータを取得、レンダリングするコストを削るため、キャッシュとして使用する目的でonload時にページの情報をreplaceStateで格納済みなので使用できない。
Sumally内を遷移後、外部サイトへ移動。その後ブラウザの戻るボタンで戻ると、必ずDOMContentLoadedを実行して欲しいのに、実行されたりされなかったりと不安定な挙動だった。また、HTMLが最新のものではなく、閲覧時のキャッシュを使用しているようだった。
header("Cache-Control:no-cache,no-store");
Back-Forwad Cache、通称bfcache(ページの戻る、進むボタン時にページを高速に描画するためにページの状態をキャッシュして復元させるブラウザの機能)が原因で起こっていた。bodyタグにonunloadを指定する、metaタグでno-cacheを指定する等、古い?情報がネットに転がってるが、現状では上記コードで全てのブラウザでbfcacheを回避出来る。
スクロール位置を復帰させるためにpopstate発行時に現在の画面のスクロール位置を保存し、ブラウザの戻るボタンで戻ってきた時に保存していた値を使用してスクロール位置を復帰させたかったが、Firefoxではpopstate発行時に画面のスクロール値を取得しようとすると正しく取得できなかった。
コストは高いが、スクロールする毎に閲覧ページのスクロール位置を保存しておき、popstate時にその値を閲覧ページのスクロール位置として保存する。
また、SafariのスワイプによるNext/Backだとpopstate発行時から500msほど遅延させてからでないとスクロール位置が復帰できなかった。
※pinterestもその方式を採用していた。
By Okuyama Yukihiko