WordPressにPjaxを実装してみる

珍しくWordpressに関する記事です。ちょっと前にこのブログにPjaxなるものを組み込んでみたんですが、Wordpressで運用されるHPにPjaxを適用するにあたってなかなか苦労したので備忘録として。

そもそもPjaxとは?

詳しくはググってもらえるとどんなものかは分かると思いますが、簡単に言うとAjaxによるページ遷移にブラウザの進む、戻るボタンを対応させたもののこと。(図1、図2参照)

図1 Ajax

図1 Ajax

図2 Pjax

図2 Pjax

Pjaxを使うメリットとしては

  • ページ全体でなく一部のみを読み込むため、転送量の削減が期待できる
  • 上記と同じ理由でページの読み込み時間も減少する
  • 書き換え面積が減るため端末の負荷も少ない
  • ページ遷移時にエフェクトをかけられる

などが挙げられるかと思います。

実装方法

ありがたいことに手軽にPjaxを扱えるjQueryプラグインを公開してくださっている方がいるので、そちらを使います。

さて、このプラグインを使ってPjaxを実装するためには、サーバーサイドとクライアントサイドの両方を書き換える必要があります。全体的な動きとしては、まずjavascriptの方で特定のリンクがクリックされたときにpjaxイベントを起こして、httpヘッダに特定文字列を付加するように記述。サーバーサイドではhttpヘッダに特定文字列があるかどうか判別し、もし有ったらpjaxイベントが発生したと判定して、ページの一部のみを返し、無かった場合は通常の読み込みと判定してページ全体を返す、といった形になります。(図3参照)

図3 Pjaxイベントのフロー

図3 Pjaxイベントのフロー

組み込み

では下記のwordpressブログを例としてPjaxを組み込んでみます。

図4 一般的なWordpressブログ

図4 一般的なWordpressブログ

WordPressのブログとしてはよくある、ブログのヘッダ部分、フッター部分をそれぞれheader.php, footer.phpに記述し、index.phpにてget_header();などで読み込んでいるという形です。とりあえず#blog_navigationと#sub_article内の<a>タグがクリックされたときにPjaxイベントを発生させ、サーバーから#main_article内のhtmlを読み込み、書き換えるという仕様とします。

まずはjquery-pjaxをheader.phpにて読み込ませましょう。jQuery自体はWordpressはデフォルトで読み込んでいるはず。

<script type="text/javascript" src="js/jquery.pjax.js"></script>

僕はwp_enqueue_script関数を使って読み込んでいます。お好きな方でどうぞ。次に特定エリア内のリンクをクリックしたらPjaxイベントが起こるようにjavascriptファイル内に記述してみます。

//#blog_navigationか#sub_article内のリンクをクリック
$(document).on('click', "#blog_navigation a, #sub_article a", function(e) {
    e.preventDefault(); //デフォルトの動作を中止
    var nextUrl = $(this).attr('href'); //リンクのURLを変数に格納
    $.pjax({ //Pjaxイベント開始
        url: nextUrl, //リクエストするURL
        container: '#main_article', //入れ替える部分
        fragment: '#main_article', //入れ替え先
        timeout:  20000
        //Pjaxがタイムアウトするまでの時間 短すぎるとPjaxが途中で中断してしまう
    });
});

さて、ここまで書くとクライアントサイドは完成したも同然です。サーバーサイドではpjaxイベントに関係無くリクエストが来たらページ全体を返しますが、jquery-pjaxはページ全部が送られてきた際にも対応しているのでpjaxによるページ遷移はちゃんとできます。折角なのでPjaxでちゃんとページ遷移が出来たかどうか確かめるためにPjaxイベントの前後にエフェクトを設定してみましょう。

//さっきのコードに追記します
$(document).on('click', "#blog_navigation a, #sub_article a", function(e) {
    e.preventDefault();
    var nextUrl = $(this).attr('href');
    //まずは切り替える部分を透明に
    $("#main_article").animate({opacity:0}, "normal", function(){
        $.pjax({//エフェクトが終わったらPjaxイベント
            url: nextUrl,
            container: '#main_article',
            fragment: '#main_article',
            timeout: 20000
        });
    });
});

//Pjaxイベントが終わったときの動作
$(document).on('pjax:end', function() {
    $("#main_article").animate({opacity:1}, "normal");
});

ちゃんと動いていればリンクをクリックすると#main_article部分がふわっと消えて、また表れたときにはページが変わっていて、ついでにurlも変わっていることが確認できると思います。これでクライアントサイドの実装は大体終わりました。

ですが、現状ではサーバーから帰ってきたページソース全体からjquery-pjaxがfragmentに設定した部分を探し出して書き換えているに過ぎません。そこで、Wordpressのindex.phpやsingle.phpにPjaxイベントが発生していたら#main_articleの部分だけを返すように設定してやります。

//index.phpやsingle.phpなど、関係ありそうなページに記述
<?php
    //http-requestヘッダに_pjaxがあるか判別
    $pjax = !($_GET['_pjax']);
    //X-PJAXが無かったら通常の読み込みと判断してサイトのヘッダも読み込む
    if ($pjax) {
        get_header();
    }
?>
<div id="main_article">
...
</div>

//同じく通常の読み込みだったらサイドバーもフッターも読み込む
<?php if ($pjax) {
    get_sidebar();
    get_footer();
}?>

上記のようにphpを書き換えると、Pjaxイベントによるhttpリクエストの時だけ#main_articleがクライアントに返されることになります。これで完成です、やったね。
実際にfragmentに設定したタグ内だけがサーバーから返ってきているか確認するには、ブラウザに付いている開発者ツール等でXHR通信を見てみると分かります。

xhr

Responseを見て目的の箇所だけ返ってきてたらOKです。もしhtml全体が返ってきてしまっている場合はphpに追加したhttp-requestヘッダを判別する箇所を

<?php
    $rheader = getallheaders();
    $pjax = (empty($rheader['X-PJAX']));
    if ($pjax) {
        // ヘッダ部を出力
        get_header();
    }
?>

に変えるとうまくいくかもしれません。こちらを使っている方も多いみたいなのですが、うちの環境では上手く動かなかったため、このブログでは$_GET[]の方を使用しています。

その他作ってて困った箇所

SNS系のボタンが効かない

Pjaxでの遷移先ページでSNS(Twitter, Facebook, etc…)の共有ボタンが効かなくなります。それもそのはず、SNSボタンはページを直接読み込んだときに処理されるからです。これはPjaxイベントが終わったときに共有ボタンを再読み込みしてあげることで解決できます。

$(document).on('pjax:complete', function() {
    twttr.widgets.load(); //Twitterボタン読み込み
    FB.XFBML.parse(); //Facebookのいいねボタン読み込み
    gapi.plusone.go(); //Google+ボタン読み込み
});

他のSNSにも再読込み関数は用意されていると思います。また、画像のスライドショープラグインなど他のjavascriptプラグインも全滅するので、その都度Pjaxイベントが終わったときに再定義や再読込をしてあげる必要があります。

ページのタイトルが変わらない

多分、上手いことちゃんとページ遷移できても、ブラウザに表示されるページタイトルは変わらないと思います。タグ読み込んでないんだから遷移先ページのタイトルを何とかしてjquery-pjaxに伝えないといけないわけです。
さて困ったぞーっとjquery-pjaxのソースを眺めていると、あるじゃないですかちゃんと! てなわけで、fragmentに設定した箇所の中にdata-title属性があると、それを遷移先ページのタイトルとして使用するみたいです。こんな感じ

<div id="main_article" data-title="<?php wp_title('',true); ?><?php if(wp_title('',false)) { ?> | <?php } ?><?php bloginfo('name'); ?>">
...
</div>

上のソースは単純にページのタイトルを出力したものです。

ブラウザの戻る/進むで時々変な挙動になる

ブラウザで戻ったり進んだりしてると時々ページが崩れたりすることがあります。今のところ未解決 (´・ω・`)

参考にさせて頂いたサイト

 

  1. はじめまして。いつも参考にさせていただいています。今回、wordpressに初めて、pjaxを入れようと思い、参考にさせていただいたのですが、どうも変数のvar nextUrlが効いていないみたいで、元の部分は消えるのですが、読み込む部分が出てきません。Urlも変化しません。

    もし、お時間があれば、ご教授いただけると幸いです。

    たけ 2013/09/09 10:45 PM

  2. たけさん、はじめまして!
    当ブログを参考にして下さっているということで記事を書いた甲斐がありました。
    ありがとうございます。

    変数nextUrlがおかしい、ということなのですが、書いて頂いた内容からするとjquery-pjaxが上手く動いていないようですね。containerに指定した要素は消える、との事ですので、
    1.fragmentに指定した要素名が間違っている、または読み込み先にその要素名が存在しない
    2.ご指摘頂いた通りnextUrlが正常にaタグのhref属性を正しく格納できていない。console.logやalertでnextUrlが正しいurlを格納しているかどうか確認してみる。
    ぱっと思いついた感じだと、この2つが確認事項として挙げられるでしょうか。それでも上手く動かないようでしたら、jquery-pjaxの基本的な書き方
    $(document).pjax(‘a’, ‘#main_article’)
    の形式で書かれてみるといいかもしれません。
    これは現ページと移動先の#main_article要素を入れ替える指定となっています。
    自分の環境ではtimeoutが短すぎて上手く動かなかった記憶があるのですが、試されてみては如何でしょうか?

    Kaya 2013/09/10 3:22 AM

  3. Kaya様

    さっそくのご返信ありがとうございました。丁寧な記述なので、いつも参考になります。ありがとうございます。

    pjax、ただ単にご紹介されていたjQueryのpluginが違うものだったので、動かなかったようです。凡ミスですみません・・・。

    無事、pjaxは動いています。もう一つ質問なのですが、pjaxで切り替わると画面がページの先頭、つまりheader部分に写ってしまうんですが、そのままの場所に留めることはできますか?

    たけ 2013/09/10 11:04 PM

  4. kaya様
    たびたびすみません。
    pjaxがうまく動いていないのが原因かも知れないです。
    最初のコンテンツが消えて、その同じコンテンツが一瞬戻って、
    コンテンツが切り替わります。。

    Timeoutの設定も十分なんですが。。何か判ればご教授いただければ幸いです。

    たけ 2013/09/10 11:16 PM

  5. たけさんお疲れ様です。
    pjax時にページの先頭に戻ってしまう、コンテンツの切り替えが上手くいかないということはpjaxイベント中に問題が発生して途中で処理が中断、移行先のURLで再読み込みしてるのかなー、と推察してみたりしますが、たけさんが仰ったような事からだけではこれ以上分からないというのが正直なところです。
    jquery-pjaxのページ下部を見るとEventという項目があるかと思いますが、これを使ってpjaxイベントの段階毎に正常に動いているかどうかconsole.logやalertで確認できますので、最後までpjaxイベントが正常に動作しているか確認してみたら如何でしょう?

    Kaya 2013/09/11 8:35 AM

  6. kayaさん

    いろいろとありがとうございます。pjaxはTimeoutを伸ばしたら、大丈夫な感じです。ただ。クリックすると、先頭に行ってしまうのはpjaxが効いていないということなのでしょうか?

    今まで見たことのあるpjaxのページがどれも、縦幅が100%のものだったので、どれが正解の動きか判断できないんです。

    お時間あれば、お返事いただけると幸いです。

    たけ 2013/09/12 10:11 PM

  7. >たけさん

    本ブログではPjaxページ遷移するときに、ページの先頭にアニメーションしてからpjaxイベントを発生させるようにしてあるのですが、このアニメーション処理を消したところ、ページ遷移するときにページの先頭になってしまいました。ですので、たけさんの方でも正常にpjaxイベントは完了しているのではないかと思います。
    (pjaxを組み込んでいるときにこの現象があったかどうか覚えていないのですが、これって制作するHPによっては非常にネックですよね…)

    Kaya 2013/09/13 6:41 PM

  8. kayaさん

    しばらくご覧になれずにすみません。いろいろとありがとうございます。そうですか。。ページ先頭になってしまうのは仕方がないんですね。あきらめます。いろいろとアドバイスありがとうございました。

    また、何かの折にはコメントさせていただきます。

    たけ 2013/09/18 10:49 PM

  9. >たけさん
    こちらこそあまりお役に立てずすいません。
    殆ど意味の無い記事ばかりのサイトですが、これからもご覧頂ければ幸いです。

    Kaya 2013/09/24 5:17 PM

Write a comment.

*の箇所は必須です