pjaxでスムーズなページ遷移(静的サイトでも手軽にpjax!)
pjaxとはAjaxでコンテンツを入れ替えるのと同時にURLも変更してくれるjQueryのプラグインの名前です。
Githubのようなスムーズでイケてるページ遷移を行います。
※内容をfalsandtru版ライブラリに合わせて書き直しました。以前のdefunkt版の記事はこちら。
このページはfalsandtru版に合わせた内容になっています。APIが結構違うのでご注意下さい。falsandtru.pjax高機能&使いやすい&現状のpjaxの問題点が色々解決されているのでオススメです(๑'―'๑)۶
javascriptの「pushState」などの新しいヒストリー関数とAjaxやサーバーサイドスクリプトを組み合わせて作られているのでjQueryプラグインじゃなくても実装は可能です。実際この技術がなんと呼ばれるのかはわかりませんが、とりあえずpjax(pushState + ajax = pjax)で浸透してるみたいです。Chrome、Firefox、IE10以降などのブラウザで動作し、非対応のブラウザでは通常のページ遷移をします。
PHPで作られた素敵なCMS「MODX」だとpjaxも非常に簡単に実装できます。
ダウンロード
jQuery本体と、jquery.pjax.jsをダウンロードします。
配布元のReadmeはかなり内容が濃いです。情報量が多いためパラメータなどの項目はこれからfalsandtru版ライブラリを使用する方向けにまとめています。
デモ
MODXを使ったサーバーサイド処理をしてpjaxリクエスト時に本文、ページタイトルを更新させています。
※MODX用のpjax説明ページの共用のデモページなのでパンくずからこのページへ戻れません。
jQuery.pjax.jsの使い方
サーバーサイドの処理にPHPを使った場合の遷移は以下の様な感じになります。
いまいち分かりにくくてすみません(´・ω・)
pjaxの設定
パラメータのareaで入れ替わる対象を設定して、特定のリンクに絞る場合はlinkを設定します。
以下の例はtarget属性がない全てのリンクを対象にしてクリックすると#pjaxContentが入れ替わります。
$.pjax({
area : '#pjaxContent', // 置き換えるコンテナのID
link : 'a:not([target])' // target属性が無いリンクを指定
});
クラス名をつけたりセレクタを指定する事で特定のエリアのリンクに絞り込む事もが出来ます。
以下の例は.mainの中の.pjaxというクラス名が付いたリンクだけを対象にします。
// セレクタを指定する場合はドキュメントを読み込んだあとに。
$(document).ready(function() {
$('.main').pjax({
area : '#pjaxContent',
link : 'a.pjax' // pjaxを行うリンクを限定(ない場合全てのリンクが対象)
});
});
サーバーサイドアプリ側の設定
pjaxからヘッダー(またはパラメータ)付きのAjaxリクエストが発生するので、PHPなどを使って「ヘッダーのX-Pjaxがtrueの場合はコンテナ部分だけを出力する」という条件で動的にHTML出力するようにします。PHPのgetallheaders()関数が使用できない場合は$_GET['pjax']または$_POST['pjax']で判定する事ができます。
<?php
$header = getallheaders();
if ($header['X-Pjax']) {
// pjaxリクエストの場合の処理&コンテナ内のHTMLだけ出力
} else {
// pjaxリクエストじゃない場合の処理&全てのHTMLを出力
}
?>
非pjaxリクエスト時に吐くHTML
<!DOCTYPE html> <head> <meta charset="UTF-8"> <title>タイトル</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"> $.pjax({ area : '#pjaxContent', link : 'a.pjax' }); </script> </head> <body> <h1>ページタイトル</h1>
<ul class="links">
<li><a href="page1.html" class="pjax">page1</a></li> <li><a href="page2.html" class="pjax">page2</a></li> <li><a href="page3.html" class="pjax">page3</a></li>
</ul> <div id="pjaxContent"> ページの内容 </div> </body> </html>
pjaxリクエスト時に吐くHTML
<title>page1</title>
<div id="pjaxContent"> ページの内容 </div>
こんなかんじで転送量をだいぶ減らせるのと、アプリ側のロジックや設定にもよりますがエリア範囲外でメニューやページリストなどを動的に吐いている場合、それらの処理をスルーできるのでサーバー側のコストも減らせそうです。
複数のエリアを設定する
areaを複数指定することで本文と見出しなど複数エリアを更新する事ができます。
ライブラリを変える前はイベントでmatchで抽出して内容書き換え…と自分でやってたんですけどデフォルトで対応しているのでとっても楽!
以下はページの内容、見出し、パンくずを更新させます。
pjaxの設定
$.pjax({
area : '#pjaxContent, h1, #breadcrumbs',
link : 'a.pjax'
});
pjaxリクエスト時に吐くHTML
タイトルとエリアで設定したタグだけ出力させます。
<title>タイトル</title>
<h1>ページタイトル</h1>
<div id="breadcrumbs">パンくずナビゲーション</div>
<div id="pjaxContent"> ページの内容 </div>
静的サイト、またはサーバー処理をしないで実装する
falsandtru版のpjaxライブラリはデフォルトでレスポンスのデータからエリアを確認するので、エリア外のコンテンツも含まれていた場合必要部分だけを抽出します。そのためサーバーサイドで分岐の処理をしなくてもpjaxを実装できます。また、動的サイトでも特にアプリ側のテンプレートの処理をしなくても手軽に実装ができます。
サーバーサイドのスクリプトを使って必要な部分だけ受け取るのと違って、全て受け取ってから必要な部分だけを抜き出すため転送量的には通常の遷移と変わりませんが体感速度は割りと早くなりました。
pjaxに対応するHTMLを書く
遷移させたいコンテンツを適当なID、以下は「pjaxContent」というIDで囲みます。
pjaxに対応している場合この中だけが遷移先のIDの中身と入れ替わるので、pjax元とpjax先のページではこのIDのコンテナが存在するようにしておきます。
$.pjax({
area : '#pjaxContent', // 置き換えるコンテナのID
link : 'a.pjax' // pjaxを行うリンクを限定(ない場合全てのリンクが対象)
});
遷移元(発火点)「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"> $.pjax({ area : '#pjaxContent', link : 'a.pjax:not([target])' }); </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でリロードするとリンクがなくなる事でコンテナだけ遷移したことが確認できると思います。
相互移動させる場合にはpage2にも$.pjax({...})の記述が必要になるので、実用するには外部ファイル化して読み込ませるのがいいと思います。
切り替えエフェクトの付け方
callbackとcallbacksの任意のプロパティにエフェクト処理をする関数を設定します。
callbacks.beforeでOUTエフェクト、callbackでINエフェクトを実行します。
ちなみに、callbackは更新処理が終わったあとに実行されるので対象エリア内のエレメントはすでに無くなっている状態です。エリア内のエレメントにアクセスしたいならタイミングはupdate.content.beforeあたりでもいいかなーと思います。色々試し中(●'◡'●)
以下はOUTで左にスライドしながらフェードアウトしてINで右からフェードインするエフェクトをつけます。
$.pjax({ area : '#pjaxContent', link : 'a.pjax:not([target])', callback : function() { $('#pjaxContent').css({left:20}).animate({ left : 0, opacity : 1 }, 100); // エフェクトの時間 }, callbacks : { before : function() { $('#pjaxContent').animate({ left : -20, opacity : 0 }, 100); } },
ajax: { timeout: 3000 }, // 読み込みにこれ以上かかる場合は通常遷移に移行 wait : 120 // エフェクト分待ち時間を作る });
OUTエフェクトがある場合切り替わり途中に内��が変わってしまったりするのでwaitパラメータで最低限の待ち時間を設定します。この数字はミリ秒なので1000で1秒です。あまり長すぎるエフェクトは逆にユーザビリティが悪化するので短めにするといいと思います。
ローディングエフェクト
同じくcallbacks.beforeでエフェクトを開始して、callbackでエフェクトを終了させます。
$.pjax({ area : '#pjaxContent', link : 'a.pjax:not([target])', callback : function() { $('div.loading').fadeOut(500); }, callbacks : { before : function() { $('div.loading').fadeIn(100); } },
ajax: { timeout: 3000 }, wait : 100 });
でも、大抵の場合ローディングエフェクトいらないぐらい早いんじゃないでしょうか…。
ページ読み込み時の処理
プロパティのcallbacks.updateでjavascriptやCSSの再読み込みを設定できますが、簡単なものはpjaxイベントで実行できます。
以下の例はページ読み込み時にリンクタグにlinkというクラスを付けます。
$(document).on('ready pjax.ready', function() {
$(".content").find("a").addClass('link');
});
パラメータとイベント
パラメータはすべてパラメータ用オブジェクトのプロパティに設定して渡します。
プロパティと登録できるイベントの一部です。イベントはかなり多いので自由度が高いです。全部引用してもしょうがないので基本的なもの&使用頻度の高そうなものをピックアップしました。(私もまだ試していないものもあるので後でちょっとだけ増やします)全てを見る場合は配布元のReadmeを見て下さい!説明文はReadmeから引用させて頂きました。
エフェクトなどを付ける場合はcallbackやcallbacksに関数を設定します。
プロパティ
area
- 値
- Selector as string / function
- 引数
- event, parameter, to, from
- 説明
- pjaxにより更新する範囲(HTML要素)をjQueryセレクタで設定します。 $.fn.pjaxにより設定されたコンテキストで絞り込まれません。
link
- 値
- Selector as string
- 説明
- pjaxによりページ移動を行うリンク(アンカータグ)をjQueryセレクタで選択します。初期値はa:not([target])(target属性がないanchor要素)です。 $.fn.pjaxで使用された場合は設定されたコンテキスト内で選択されます。 なお、カレントディレクトリを基準とした相対パスは正常に動作しない可能性があるため使用しないでください。
scrollTop
- 引数
- event, parameter, to, from
- 説明
- リンクまたはフォームによるページ移動後の縦方向のスクロール位置を設定します。falseまたはnullを設定すると移動前のスクロール位置を維持します。関数を設定すると戻り値がスクロール位置となります。初期値は0です。
wait
- 値
- Millisecond as number / function
- 引数
- event, parameter, to, from
- 説明
- $.ajaxの実行からコンテンツの更新までの最低待ち時間を設定します。関数が設定された場合は関数の戻り値が使用されます。jQuery1.5より前のバージョンでは無効です。初期値は0です。
callback
- 値
- function
- 引数
- event, parameter, data, textStatus, XMLHttpRequest
- 説明
- ページ移動後に実行されるコールバック関数を設定します。ページの更新処理が成功したときにupdate.complete( event, parameter, data, textStatus, XMLHttpRequest )の直後に実行されます。callback、callbacksともにcallbacks.asyncにtrueを設定することでコールバック関数の実行を非同期に行えます。コールバック関数を非同期で実行することで処理を高速化することができますが、戻り値にfalseを設定することによる処理のキャンセルができなくなります。
callbacks
- 説明
- ajaxを除くすべてのコールバック関数は戻り値にfalseを設定することで現在の処理を抜けることができます。beforeでは以降の処理をすべてキャンセルします。このときフォールバック処理はfallbackの設定にかかわらず行われません。update.any.beforeupdate.any.afterではページ更新処理のうちanyの示す部分の更新処理をキャンセルないし抜けます。ページ移動でエラーが発生した際にupdate.errorupdate.completeで処理を抜けるとフォールバック処理がfallbackの設定にかかわらず行われません。 使用できるcallbacksのプロパティと渡されるパラメータ、実行タイミングは次のとおりです。(以下callbacks参照)
callbacks(イベント処理の登録)
記述の仕方
.以下は.前のオブジェクトのプロパティになるので:{}でネストします。
$.pjax({ callbacks : { update : { complete : function(event, arg) { console.log(arg + ': update.complete'); } } } });
before
- 引数
- event, parameter
after
- 引数
- event, parameter
ajax.beforeSend
- 引数
- event, parameter, data, settings
ajax.dataFilter
- 引数
- event, parameter, data, type
ajax.error
- 引数
- event, parameter, XMLHttpRequest, textStatus, errorThrown
update.before
- 引数
- event, parameter, data, textStatus, XMLHttpRequest
update.after
- 引数
- event, parameter, data, textStatus, XMLHttpRequest
update.success
- 引数
- event, parameter, data, textStatus, XMLHttpRequest
update.error
- 引数
- event, parameter, data, textStatus, XMLHttpRequest
update.complete
- 引数
- event, parameter, data, textStatus, XMLHttpRequest