defunkt版 Pjax

更新日
2017.02.15
作成日
2014.05.11

シームレスなページ遷移が可能になる defunkt版 の Pjax の説明です。

ダウンロード

jQuery本体と、jquery.pjax.jsをダウンロードします。

※2017年1月現在 jQuery3 ではエラーが出てそのままでは使用できないようです。

jQuery.pjax.jsの基本的な使い方

サーバーサイドの処理にPHPを使った場合の遷移は以下の様な感じになります。

1.0から譲渡でイベントを発生させているようなので、リンクの親要素をイベント発生源にします。(documentやbodyなど)jQuery本体とjquery.pjax.jsを読み込んで、documentや$(document).readyで親要素に対してPjaxを割り当てます。

$(document).pjax('a.pjax', '#pjaxContent');

documentの中にあるpjaxというクラス名が付いたリンクをクリックすると#pjaxContentが入れ替わります。

Pjaxからヘッダー(またはパラメータ)付きのAjaxリクエストが発生するので、PHPなどを使って「ヘッダーのX-PJAXがtrueの場合はコンテナ部分だけを出力する」という条件で動的にHTML出力するようにします。PHPのgetallheaders()関数が使用できない場合は$_GET['_pjax']で判定する事ができます。

<?php
$header = getallheaders();
if ($header['X-PJAX']) {
  // pjaxリクエストの場合の処理&コンテナ内のHTMLだけ出力
} else {
  // pjaxリクエストじゃない場合の処理&全てのHTMLを出力
}
?>

サーバー処理を使わず静的サイトで手軽にpjaxしてみる

jquery.pjax.jsに結果の一部を抽出できる「fragment」パラメータが用意されているので、これを使用する事でサーバー側の処理を使わずに静的HTMLでpjaxを実装することも出来そうなので試してみました。

サーバー処理を使わずに「fragment」だけで実装した場合の遷移は簡単に説明すると以下のようになります。

Ajaxで全てのページ内容を取得したあとに必要な部分だけを取り出します。

サーバーサイドのスクリプトを使って必要な部分だけ受け取るのと違って、全て受け取ってから必要な部分だけを抜き出すため転送量的には通常の遷移と変わりませんが体感速度は割りと早くなりました。検索エンジンライクなので「CMSとか使ってないけどエフェクトとかローディングとか入れてこじゃれたサイトを作りたい」という場合には実装してみるのも良いかも...?

ちなみに、fragmentはサーバー処理をしつつさらに抽出するということももちろん出来ます。例えばMODXのcontent内の一部をPjaxで遷移させるという事も可能です。(Dittoの解説ページがそんな感じになっています)

Pjaxに対応するHTMLを書く

遷移させたいコンテンツを適当なID、以下は「pjaxContent」というIDで囲みます。
Pjaxに対応している場合この中だけが遷移先のIDの中身と入れ替わるので、Pjax元とPjax先のページではこのIDのコンテナが存在するようにしておきます。

$(document).pjax('a.pjax', {
  container:'#pjaxContent', // 置き換えるコンテナのID
  fragment: '#pjaxContent'  // 取り出すコンテナのID 相互に遷移させる場合はcontainerと同じもの
});

遷移元(発火点)「page1」のHTML

<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<title>page1</title>
<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript" src="js/jquery.pjax.js"></script>
<script type="text/javascript">
$(document).pjax('a.pjax', {
  container:'#pjaxContent', // 置き換えるID
  fragment: '#pjaxContent'  // 取り出すID
});
</script>
</head>
<body>
<h1>pjaxテスト</h1>
<ul class="links"> <li><a href="page2.html" class="pjax">page2</a></li> <li><a href="page3.html" class="pjax">page3</a></li>
</ul> <div id="pjaxContent"> <h2>ここはページ1</h2> <div class="main">ページ1の中身</div> </div> </body> </html>

遷移先「page2」のHTML

<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<title>page1</title>
<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript" src="js/jquery.pjax.js"></script>
</head>
<body>
<h1>pjaxテスト</h1>
<div id="pjaxContent">
  <h2>ここはページ2</h2>
  <div class="main">ページ2の中身</div>
</div>
</body>
</html>

処理が早いとちゃんと遷移しているのか分かりにくいですが、page2でリロードするとリンクがなくなる事でコンテナだけ遷移したことが確認できると思います。

切り替えエフェクトの付け方

基本的にはpjaxのイベントに関連付ける事でエフェクトや処理を追加する事ができます。

Inエフェクト

スライドしながらフェードインするエフェクトをつける例です。

$(document).on('pjax:end', function() {
	$('#pjaxContent').animate({
		left : 0,
		opacity : 1
	}, 'fast');
});

Outエフェクト

100pxスライドしながらフェードアウトするエフェクトをつける例です。
Outエフェクトの場合pjax:startなどに関連付けてもエフェクトが飛んでしまったり、アニメーション中に内容が切り替わってしまう場合があります。Outエフェクトが完全に終わったあとにpjaxを実行させたい場合は、クリックイベントのアニメーションのコールバックで$.pjaxを使用するのが簡単かなーと思います。
エフェクトの終わりを待つ場合、時間を余分に考えてtimeoutを指定しないとタイムアウトして通常遷移してしまう場合があります。
クリックイベント内でpjax対応ブラウザのみ処理をする場合は$.support.pjaxで分岐できます。

$(document).on('click', 'a.pjax', function(event) {
	event.preventDefault();
	var href = $(this).attr('href');
	// フェードアウトエフェクトの処理
	$('#pjaxContent').animate({
		left : -100,
		opacity : 0
	}, 'fast', function() {
		// コールバックでpjaxを実行する
		$.pjax({
			url: href,
			container : '#pjaxContent',
			fragment : '#pjaxContent',
			timeout : 1000
		});
	});
});

ローディング中エフェクト

pjax:sendでエフェクトを開始して、pjax:completeでエフェクトを終了させます。

$(document).on('pjax:send', function() {
  $('#loading').show();
})
$(document).on('pjax:complete', function() {
  $('#loading').hide();
})

pjaxのイベント

以下のようなイベントが登録されています。
エフェクトなどを付ける場合はコンテナのこのイベントにエフェクト処理をバインドします。

UTF-8以外の文字化け対策

UTF-8以外のコードをajaxで読み込むと文字化けを起こすため、HTMLをShift_JISなどで書いている場合はpjax:beforeSendイベントでXMLHttpRequest用の文字コードを設定します。

  $(document).on('pjax:beforeSend', function(e, xhr){
      xhr.overrideMimeType("text/html;charset=Shift_JIS");
  });

<base href>が設定されている時挙動がおかしくなる対処

jQuery.pjax.jsでは、HTMLに<base href>が設定されている時、発火点に戻るとBaseHrefのURLに戻ってしまう。
pjax294行め付近の以下の行を

 window.history.replaceState(pjax.state, document.title)

以下のように書き換えました。あとでもう少し調べてみようかとおもいます。

 window.history.replaceState(pjax.state, document.title, pjax.state.url)

一方通行にした場合のChrome対策

遷移先でpjaxが読み込まれていない時に、chromeではpage2でリロードを行った後にブラウザで戻ると正常なコンテンツを表示できなくなりました。遷移先に以下のような処理を加えることで回避出来るようです。

if ( $.inArray('state', $.event.props) < 0 ) $.event.props.push('state')

jquery.pjax.jsのルートで同じ処理を行なっているのでjquery.pjax.jsを読み込むだけでも回避は出来るようです。