wordpress WordPress で Infinite Scroll を使ってみる

2013年9月21日

WordPress で Infinite Scroll を使った際のメモ。

Infinite Scroll には WordPress 用のプラグインもあるが、それは使わず jQuery のプラグインを使用。使用したページはカスタム投稿タイプのアーカイブページ(archive-xxxx.php や taxonomy-xxxx_cat.php)でページネーションで次の投稿へのリンクがあるページ。

ダウンロード

Infinite Scroll を配布ページからダウンロード(ページ右側の「Download ZIP」をクリック)して、その中の「jquery.infinitescroll.min.js」を適当なディレクトリに配置。

Infinite Scroll の読み込み

Infinite Scroll を header.php や footer.php で読み込む場合。

<script src="<?php echo get_template_directory_uri(); ?>/js/jquery.infinitescroll.min.js"></script>

Infinite Scroll を functions.php で読み込む場合。以下は、カスタム投稿タイプ「works」のアーカイブページと「news_cat」のタクソノミーページの場合に読み込む例。(Infinite Scroll には jQuery が必要 )

function add_my_scripts() {  
  if(is_admin()) return; 
  wp_enqueue_script('jquery'); //WordPress内蔵の jQuery をロード(省略可能)  

  if(is_post_type_archive( 'works' ) || is_tax('news_cat') ){
    wp_enqueue_script('infinite', //「infinitescroll」をロード
    get_template_directory_uri(). '/js/jquery.infinitescroll.min.js',
    array('jquery'),
    '20130920' 
    );
  }  
}
add_action('wp_print_scripts', 'add_my_scripts');

Infinite Scroll の設定を記述

以下のような構造のテンプレートを使用した場合の例。(この例の場合、footer.php では body とhtml の閉じタグを記述していないので、フッターの読み込みの後に記述)

<?php get_header(); ?>
<div id="content">
<?php if(have_posts()): while(have_posts()): the_post(); ?>

<div class="work">//この部分をロードする
<h3><a href="<?php the_permalink(); ?>" ?>"><?php the_title(); ?></a></h3>
・・・省略・・・
</div><!-- end of .work -->

<?php endwhile; endif; ?>
<?php wp_reset_postdata(); ?>

<?php pagenavi(); ?>

</div><!-- end of #content -->
<?php get_sidebar(); ?>
<?php get_footer('xxxx'); ?>

<script type="text/javascript">
jQuery(function($){
    $('#content').infinitescroll({
        navSelector  : ".pagenavi",     // ナビゲーション要素を指定       
        nextSelector : ".pagenavi a:last", // ナビゲーションの「次へ」の要素を指定
        itemSelector : ".work"   // 表示させる要素を指定  
    });  
});  
</script>
</body>
</html>

スクリプトでロードする領域を囲む要素(プラグインを適用したい要素)を jQuery のセレクタで指定

上記の場合、「$(‘#content’).infinitescroll」として「#content」を指定。

最低限以下の3つの項目の設定が必要

  • navSelector: ページネーションのセレクタ(次に読み込む要素へのリンクを囲んでいる要素)
  • nextSelector: 上記の中の「次のページヘ」を示すリンクのセレクタ
  • itemselector: 表示させたい要素のセレクタ

navSelector

上記の例の場合、ページネーションは「pagenavi()」という関数を使っているが、その関数が出力する div 要素のクラス名が「.pagenavi」なので「navSelector」には「.pagenavi」を指定。

nextSelector

ページネーションの中で「次のページヘ」を示すリンクのセレクタは、複数ある a 要素の最後の要素なので「.pagenavi a:last」を指定。(これは向きにもよる)

以下は「pagenavi()」が出力する内容。(「.pagenavi a.next」でもOK)

<div class="pagenavi">
  <span class="page-numbers current">1</span>
  <a class="page-numbers" href="http://localhost/jp/works/page/2/">2</a>
  <a class="page-numbers" href="http://localhost/jp/works/page/3/">3</a>
  <a class="page-numbers" href="http://localhost/jp/works/page/4/">4</a>
  <span class="page-numbers dots">…</span>
  <a class="page-numbers" href="http://localhost/jp/works/page/13/">13</a>
  <a class="next page-numbers" href="http://localhost/jp/works/page/2/">次へ »</a>
</div>

itemselector

表示させたい領域はコンテンツ部分で上記では「<div class=”work”>」なので、それを指定。

オプションの指定

オプションは「github の wiki」に記載がある。

通常のオプション(Regular Options)以外は、「loading:」、「state:」などの指定(プロパティが入れ子)が必要。

ロード中やロード終了後に表示される文字列は「Regular Options」ではないので、以下のように記述しないとうまく行かない。

<script type="text/javascript">
jQuery(function($){
    $('#content').infinitescroll({
  loading: {
          finishedMsg: "<span class='finished_message'>リストの最後に到達しました。</span>",
          msgText: "<span class='message_text'>ロード中です・・・</span>"  //最後の項目の後にはカンマを付けない
       },
  navSelector  : ".pagenavi",           
  nextSelector : ".pagenavi a:last",
  itemSelector : ".news_contents",
  animate : true,  
  extraScrollPx: 100   //最後の項目の後にはカンマを付けない 
    });  
});  
</script>
  • msgText: ロード中に表示される文字列(loading:に記述)
  • finishedMsg: ロード終了後に表示される文字列(loading:に記述)
  • animate : ロード終了後スクロールのアニメーションを行うかどうか(デフォルトは true)
  • extraScrollPx: アニメーションでスクロールする量(ピクセルで指定。デフォルトの値は150)animate を true にした場合に有効
  • bufferPx:スクロールして最後に近づく際にロードを開始する距離のバッファ(?)。この値を大きくすると最後に近づく前にロードが始まる。

記述例は「paulirish/infinite-scroll」にある。

ロード中のメッセージや画像、終了後のメッセージは div 要素(id=”infscr-loading”)に出力されるので CSS でデザインを指定でき、また「msgText」「finishedMsg」に独自にクラスを追加して CSS でデザインを指定することも可能。

//最後に到達した後の出力の例
<div id="infscr-loading" style="display: none;">
<img src="data:image/gif;base64,R0..省略..AgA7" alt="Loading..." style="display: none;">
<div style="opacity: 1;">
<span class="finished_message">リストの最後に到達しました。</span>
</div>
</div>

以下は CSS の例

#infscr-loading {
  clear: both;
  font-size: 11px; font-size: 1.1rem;  
  padding-left: 20px;
}
span.finished_message, span.message_text {
  color: #999;
}
span.message_text{
  margin-left: 5px;
}

注意点

  • オプションを指定する際、最後の項目の後に「,」(カンマ)が付いていると、IE7 などでは Infinite Scroll が起動しない。その場合、コンソールを見ると「SCRIPT1028: 識別子、文字列または数がありません。」のようなエラーが出ている。
  • 「msgText」、「finishedMsg」は Infinite Scroll の内部でクラス名として使用されているので、同じクラス名を使用すると表示に問題が発生する可能性がある。

一度に読み込む件数

デフォルトでは「設定」→「表示設定」→「1ページに表示する最大投稿数」で指定してある件数かループでの「numberposts」で指定した件数が読み込まれる。

または、functions.php に以下のように記述して件数を指定する。以下は、カスタム投稿タイプ「works」のアーカイブページと「news_cat」のタクソノミーページの場合は、4件読み込む場合の指定。

add_action( 'pre_get_posts', 'modify_my_main_queries' ); 
function modify_my_main_queries ( $query ) {
  if ( ! is_admin() && $query->is_main_query() ) {
    if ($query-> is_post_type_archive( 'works' ) || $query->is_tax('works_tag') ) {
      $query->set('posts_per_page', 4 );
    }
  }
}

パーツテンプレート

複数のカスタム投稿タイプのテンプレートで使う場合、オプションがそれぞれ異なると似たような記述を複数することになるので、Infinite Scroll の部分をパーツテンプレートとして作成し、それぞれのカスタム投稿タイプのテンプレートにはオプションを記述するようにしてみる。

  • カスタム投稿タイプのテンプレートに id や class を使ってオプションを記述し、その部分を CSS で非表示に
  • パーツテンプレートに infinitescroll の設定を記述し、上記のオプションを読み込む
  • オプションの指定がなければデフォルト値を設定

カスタム投稿タイプのテンプレートの抜粋

<?php get_header(); ?>
<div id="content">
・・・省略・・・
</div><!-- end of #content -->
<?php get_sidebar(); ?>
<?php get_footer('works'); ?>
<div id="infinitescrollMsgs"><!-- この部分は CSS で非表示に -->
<span class="finishedMessage">全てのリストを読み込みました。</span>
<span class="messageText">リストを読み込み中・・・</span>
<span class="item_selector">.work</span>
<span class="buffer_px">150</span>  
<span class="animate_boolean"></span>
<span class="extra_scroll_px"></span>
</div><!-- end of #infinitescrollMsg -->
<?php get_template_part('infinitescroll'); ?><!-- パーツテンプレートの読み込み -->
</body>
</html>

CSS

#infinitescrollMsgs {
  display: none;
}

パーツテンプレート

//infinitescroll.php
<script type="text/javascript">
jQuery(function($){
  var fm = $('#infinitescrollMsgs span.finishedMessage').html();
  var mt = $('#infinitescrollMsgs span.messageText').html();
  var is = $('#infinitescrollMsgs span.item_selector').text();  
  var ns = $('#infinitescrollMsgs span.nav_selector').text();  
  var nxs = $('#infinitescrollMsgs span.next_selector').text();  
  var anim =  $('#infinitescrollMsgs span.animate_boolean').text();  
  var bp = parseInt($('#infinitescrollMsgs span.buffer_px').text(), 10);
  var esp = parseInt($('#infinitescrollMsgs span.extra_scroll_px').text(), 10);  
  var settings = {    //オプションの指定がなければデフォルトを適用
    page_fm : fm || "<em>You've reached the end of the content.</em>",
    page_mt : mt || "<em>Loading the next set of posts...</em>",
    page_ns : ns || ".pagenavi", 
    page_nxs:  nxs || ".pagenavi a.next",
    page_is: is || "div.post",
    page_anim : anim || false,
    page_bp : bp || 40,
    page_esp : esp || 150
  };  
    $('#content').infinitescroll({
    loading: {
        finishedMsg: settings.page_fm,
        msgText: settings.page_mt
       },
        navSelector  : settings.page_ns,           
        nextSelector : settings.page_nxs,
        itemSelector : settings.page_is,
        animate: settings.page_anim,
        extraScrollPx: settings.page_esp,
        bufferPx: settings.page_bp  
    });  
});  
</script>

Call Back の指定

読み込んだ後に何らかの処理をする場合の Call Back (コールバック)関数の指定の例。

「loading:」のオプションに「finished」があるのでこれを利用

finished

function

Default: undefined

The finished function is called after each AJAX call that loads new content, after the loading message is displayed. If the option is not overridden, or a falsy value is passed it, the default action will be to fade-out the loading message.

新しい要素を読み込むごとに(ロード中というメッセージの後に)呼びだされる関数(Call Back)を指定できるみたい。デフォルトは「undefined」。

指定するコールバックは、特定のページにのみ使用するので、前述の Infinite Scroll の部分のパーツテンプレートにコールバックを使用するかどうかを指定する項目を追加。

HTML

<?php get_footer('works'); ?>
<div id="infinitescrollMsgs">
<span class="finishedMessage">全てのリストを読み込みました。</span>
<span class="messageText">リストを読み込み中・・・</span>
<span class="item_selector">.work</span>
<span class="buffer_px">150</span>  
<span class="animate_boolean"></span>
<span class="extra_scroll_px"></span>
<span class="call_back">true</span><!-- 追加した項目 -->
</div><!-- end of #infinitescrollMsg -->
<?php get_template_part('infinitescroll'); ?>

<span class="call_back">true</span>

のように「true」を指定した場合のみコールバック関数を実行するようにする。

コールバック関数の指定

以下は「loading」オプションに追加するコールバック関数の指定「finished:」

jquery.infinitescroll.js の中身を見てみると、オプション(opts)をパラメータに取ることができるみたいなので、それを利用。

また、コールバックが完了した時点で「ロード中」の画像をすぐに非表示にしたいので、そのための記述「opts.loading.msg.fadeOut(opts.loading.speed);」を入れる。
(これを記述しないと、次のロード中の画像が出るまで表示された状態になってしまった)

if(settings.page_cb == "true") は <span class="call_back"></span> が「true」に指定されていればということ。

finished: function(opts) {
    if(settings.page_cb == "true") {
      実行したい処理を記述
      opts.loading.msg.fadeOut(opts.loading.speed);                
    }else {
      opts.loading.msg.fadeOut(opts.loading.speed);
    }
  }

以下は読み込んだ要素(recent_work クラスの要素)の高さを揃える場合の例。

関連情報:「jQuery を使って要素の高さを揃える

finished: function(opts) {
    if(settings.page_cb == "true") {
      var recent_work$ = $('.recent_work');
      var recent_work_length = recent_work$.length;  
      if(recent_work_length !== 0) {
        for(var i = 0 ; i < Math.ceil(recent_work_length / 2) ; i++) {
          var maxHeight = 0;
          for(var j = 0; j < 2; j++){
            if (recent_work$.eq(i * 2 + j).height() > maxHeight) { 
              maxHeight = recent_work$.eq(i * 2 + j).height(); 
            }
          }
          for(var k = 0; k < 2; k++){
              recent_work$.eq(i * 2 + k).height(maxHeight); 
          }
        } 
      }
      opts.loading.msg.fadeOut(opts.loading.speed);          
    }else {
      opts.loading.msg.fadeOut(opts.loading.speed);
    }
  }

以下がパーツテンプレートの記述全体。

パーツテンプレート infinitescroll.php

jQuery(function($){
  var fm = $('#infinitescrollMsgs span.finishedMessage').html();
  var mt = $('#infinitescrollMsgs span.messageText').html();
  var is = $('#infinitescrollMsgs span.item_selector').text();  
  var ns = $('#infinitescrollMsgs span.nav_selector').text();  
  var nxs = $('#infinitescrollMsgs span.next_selector').text();  
  var anim =  $('#infinitescrollMsgs span.animate_boolean').text();  
  var bp = parseInt($('#infinitescrollMsgs span.buffer_px').text(), 10);
  var esp = parseInt($('#infinitescrollMsgs span.extra_scroll_px').text(), 10);  
  var cb = $('#infinitescrollMsgs span.call_back').text();    //追加

  var settings = {
    page_fm : fm || "<em>You've reached the end of the content.</em>",
    page_mt : mt || "<em>Loading the next set of posts...</em>",
    page_ns : ns || ".pagenavi", 
    page_nxs:  nxs || ".pagenavi a.next",
    page_is: is || "div.post",
    page_anim : anim || false,
    page_bp : bp || 40,
    page_esp : esp || 150,
    page_cb : cb || false     //追加
  };  

    $('#content').infinitescroll({
    loading: {
      finishedMsg: settings.page_fm,
      msgText: settings.page_mt,
      //以下が追加部分
      finished: function(opts) {
        if(settings.page_cb == "true") {
          var recent_work$ = $('.recent_work');
          var recent_work_length = recent_work$.length;  
          if(recent_work_length !== 0) {
            for(var i = 0 ; i < Math.ceil(recent_work_length / 2) ; i++) {
              var maxHeight = 0;
              for(var j = 0; j < 2; j++){
                if (recent_work$.eq(i * 2 + j).height() > maxHeight) { 
                  maxHeight = recent_work$.eq(i * 2 + j).height(); 
                }
              }
              for(var k = 0; k < 2; k++){
                  recent_work$.eq(i * 2 + k).height(maxHeight); 
              }
            }             
          }
          opts.loading.msg.fadeOut(opts.loading.speed);          
        }else {
          opts.loading.msg.fadeOut(opts.loading.speed);
        }
      }
      //ここまで    
    },
    debug: true,    //コンソールへの出力も追加して確認
    navSelector  : settings.page_ns,           
    nextSelector : settings.page_nxs,
    itemSelector : settings.page_is,
    animate: settings.page_anim,
    extraScrollPx: settings.page_esp,
    bufferPx: settings.page_bp  
    });    
  
});