WordPressのブログやメディアサイトで、ページネーションではなく無限スクロールを導入すると、ユーザーの体験が向上します。この記事では、Ajaxを使って無限スクロールを実現する方法を具体的に解説します。
JavaScriptでAjaxリクエストを送信
無限スクロールを実現するJavaScriptを用意します。jQueryを使用して、スクロール時に次の記事を非同期で取得します。
NG:読み込み中フラグが無く多重リクエストが飛ぶ
jQuery(document).ready(function ($) {
let currentPage = 1; // 現在のページ
const maxPage = parseInt(ajax_object.max_page); // 最大ページ数
const ajaxUrl = ajax_object.ajax_url; // Ajax URL
// 無限スクロール
$(window).on('scroll', function () {
if ($(window).scrollTop() + $(window).height() >= $(document).height() - 300) {
if (currentPage < maxPage) {
currentPage++;
$.ajax({
url: ajaxUrl,
method: 'POST',
data: {
action: 'load_more_posts',
page: currentPage,
},
beforeSend: function () {
$('#loading-icon').show(); // ローディングアイコンを表示
},
success: function (response) {
if (response) {
$('#post-container').append(response); // 記事を追加
$('#loading-icon').hide(); // ローディングアイコンを非表示
} else {
$('#loading-icon').hide();
}
},
});
}
}
});
});
// 読み込み中フラグが無いため、scrollイベントの多重発火で複数ページ分のリクエストが同時に飛ぶ
読み込み中フラグが無いと同時に複数ページ分のリクエストが飛ぶ
scrollイベントは短時間に何度も発火します。前のAjaxリクエストが完了して$(document).height()が伸びる(=トリガーゾーンから外れる)前に、次のscrollイベントが発火するとcurrentPageが連続でインクリメントされ、複数ページ分のリクエストが同時に送信されてしまいます(ページ飛ばし・記事の重複表示・無駄なサーバー負荷の原因)。isLoadingのようなフラグでリクエスト中の再入力をガードする必要があります。OK:isLoadingフラグでガード
jQuery(document).ready(function ($) {
let currentPage = 1; // 現在のページ
let isLoading = false; // 読み込み中フラグ
const maxPage = parseInt(ajax_object.max_page); // 最大ページ数
const ajaxUrl = ajax_object.ajax_url; // Ajax URL
// 無限スクロール
$(window).on('scroll', function () {
if (isLoading) return; // 読み込み中は何もしない
if ($(window).scrollTop() + $(window).height() >= $(document).height() - 300) {
if (currentPage < maxPage) {
isLoading = true;
currentPage++;
$.ajax({
url: ajaxUrl,
method: 'POST',
data: {
action: 'load_more_posts',
page: currentPage,
},
beforeSend: function () {
$('#loading-icon').show(); // ローディングアイコンを表示
},
success: function (response) {
if (response) {
$('#post-container').append(response); // 記事を追加
}
},
complete: function () {
// 成功・失敗にかかわらず必ず後始末する
$('#loading-icon').hide();
isLoading = false;
},
});
}
}
});
});
上記コードでは、スクロール位置がページ下部に近づいたとき、読み込み中でなければ次の記事を取得して表示します。
PHPで記事データを返す
functions.php に以下のコードを追加して、Ajaxリクエストを受け取り、記事データを返す処理を作成します。
functions.php:Ajaxハンドラーで記事データを返す
function load_more_posts() {
$paged = isset($_POST['page']) ? intval($_POST['page']) : 1;
$query_args = array(
'post_type' => 'post',
'posts_per_page' => 5,
'paged' => $paged,
);
$query = new WP_Query($query_args);
if ($query->have_posts()) {
while ($query->have_posts()) {
$query->the_post();
echo '<article class="post">';
echo '<h2><a href="' . esc_url(get_permalink()) . '">' . esc_html(get_the_title()) . '</a></h2>';
echo '<p>' . esc_html(get_the_excerpt()) . '</p>';
echo '</article>';
}
}
wp_reset_postdata();
wp_die();
}
add_action('wp_ajax_load_more_posts', 'load_more_posts');
add_action('wp_ajax_nopriv_load_more_posts', 'load_more_posts');
このコードは、指定されたページ番号に基づいて記事データを取得し、HTML形式で返します。
Ajax用のデータを渡す
functions.php に以下のスクリプトを追加して、フロントエンドに必要なデータを渡します。
NG:子テーマ環境ではJSファイルが404になる
function enqueue_infinite_scroll_scripts() {
wp_enqueue_script('infinite-scroll', get_template_directory_uri() . '/js/infinite-scroll.js', array('jquery'), null, true);
wp_localize_script('infinite-scroll', 'ajax_object', array(
'ajax_url' => admin_url('admin-ajax.php'),
'max_page' => ceil(wp_count_posts()->publish / 5), // 最大ページ数
));
}
add_action('wp_enqueue_scripts', 'enqueue_infinite_scroll_scripts');
// 子テーマ環境ではURLが親テーマを指してしまい infinite-scroll.js が404になる
get_template_directory_uri() は親テーマのURLを返す
get_template_directory_uri()は親テーマのディレクトリURLを返す関数です。子テーマを使っている環境でinfinite-scroll.jsを子テーマ側に置くと、URLが親テーマを指してしまいスクリプトが404になり、無限スクロールが一切動作しません。子テーマのURLを正しく取得するにはget_stylesheet_directory_uri()を使います。OK:get_stylesheet_directory_uriで子テーマでも正しく読み込む
function enqueue_infinite_scroll_scripts() {
wp_enqueue_script('infinite-scroll', get_stylesheet_directory_uri() . '/js/infinite-scroll.js', array('jquery'), null, true);
wp_localize_script('infinite-scroll', 'ajax_object', array(
'ajax_url' => admin_url('admin-ajax.php'),
'max_page' => ceil(wp_count_posts()->publish / 5), // 最大ページ数
));
}
add_action('wp_enqueue_scripts', 'enqueue_infinite_scroll_scripts');
これにより、ajax_object 変数を通じて ajax_url と max_page をJavaScriptに渡します。
HTMLで投稿リストを表示
記事リストを表示するHTMLを用意します。
page.php:初期表示の投稿リスト
<div id="post-container">
<?php
$query = new WP_Query(array(
'post_type' => 'post',
'posts_per_page' => 5,
'paged' => 1,
));
if ($query->have_posts()) {
while ($query->have_posts()) {
$query->the_post();
?>
<article class="post">
<h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
<p><?php the_excerpt(); ?></p>
</article>
<?php
}
wp_reset_postdata();
}
?>
</div>
<div id="loading-icon" style="display: none;">
<p>読み込み中...</p>
</div>
このコードでは、最初の5件の記事を表示し、Ajaxで読み込んだ記事を #post-container に追加します。
スタイルの調整
ローディングアイコンのデザインを追加します。
style.css:ローディングアイコンのスタイル
#loading-icon {
text-align: center;
margin: 20px 0;
font-size: 14px;
color: #555;
}
関連記事
よくある質問(FAQ)
QWordPressでAjaxを使った無限スクロールの基本的な仕組みは?
Aスクロールイベントでページ末尾を検知し、
jQuery.ajax()でwp-admin/admin-ajax.phpにリクエストを送ります。PHPのAjaxハンドラーでWP_Queryを実行して記事HTMLを返し、JavaScriptがDOMに追加します。リクエスト中に再度リクエストが飛ばないよう、読み込み中フラグでガードするのがポイントです。Qwp_localize_script()でAjaxのURLをJavaScriptに渡すには?
A
wp_localize_script('my-script', 'myVars', array('ajaxUrl' => admin_url('admin-ajax.php')))でJavaScript内からmyVars.ajaxUrlとしてAjaxエンドポイントURLにアクセスできます。子テーマを使っている場合はスクリプトのURL取得にget_stylesheet_directory_uri()を使ってください。QAjaxの無限スクロールで最後のページ以降のリクエストを止めるには?
APHPのAjaxハンドラーで
WP_Queryのmax_num_pagesと現在のページ数を比較し、最後のページの場合はhas_more: falseのようなフラグをJSONで返します。JavaScriptでこのフラグを確認してイベントリスナーを解除します。まとめ
WordPressでAjaxを使った無限スクロールを実装することで、ユーザー体験を向上させることができます。子テーマ環境ではget_stylesheet_directory_uri()を使うこと、スクロールイベントには読み込み中フラグでガードを入れることの2点に注意すれば、この方法はシンプルながら柔軟性が高く、カスタマイズも容易です。
