WordPressテーマ「SWELL」で、記事一覧に「もっと見る」ボタンを設置して、非同期で読み込む無限スクロール(Load Moreボタン型)を実装した手順をまとめます。
SWELLで記事一覧ページを「もっと見る」ボタン付き無限スクロールにする方法
- 通常の投稿のカテゴリー・タグアーカイブが対象(カスタム分類は対象外)
- 3件ずつ追加で読み込み
- ページネーションを残して失敗時も移動可能
- コードはすべて
functions.phpに記述済み
表示設定の変更
記事一覧で表示する数を変更します。
検証では「設定 → 表示設定 → 1ページに表示する最大投稿数」を3件にしましたが、本番では希望する件数を指定できます。

functions.phpにコードを追加
有効な子テーマのfunctions.phpをバックアップして追加するか、Code Snippetsへ登録します。どちらか一方だけに追加し、親テーマは編集しません。
コードをいれるだけです。
add_action('wp_footer', 'my_add_loadmore_button');
function my_add_loadmore_button() {
if ( ! is_category() && ! is_tag() ) return;
global $wp_query;
$term = get_queried_object();
$max = $wp_query->max_num_pages;
$paged = max( 1, get_query_var( 'paged' ) );
$term_id = $term->term_id ?? 0;
$taxonomy = $term->taxonomy ?? '';
if ( $max <= 1 || ! $term_id ) return;
$list_type = get_term_meta($term_id, 'swell_term_meta_list_type', true) ?: 'card';
?>
<script>
document.addEventListener('DOMContentLoaded', function () {
const $ul = document.querySelector('#main_content .p-archiveContent .p-postList, #main_content .p-termContent .p-postList');
if (!$ul) return;
const btn = document.createElement('button');
btn.type = 'button';
btn.id = 'load-more';
btn.className = 'swell-block-button__link';
btn.dataset.paged = <?php echo $paged + 1; ?>;
btn.dataset.termId = '<?php echo esc_js( $term_id ); ?>';
btn.dataset.taxonomy = '<?php echo esc_js( $taxonomy ); ?>';
btn.dataset.maxPages = '<?php echo esc_js( $max ); ?>';
btn.dataset.listType = '<?php echo esc_js( $list_type ); ?>';
btn.innerHTML = '<span>もっと見る</span>';
const wrapper = document.createElement('div');
wrapper.className = 'swell-block-button is-style-btn_normal';
wrapper.appendChild(btn);
$ul.parentNode.appendChild(wrapper);
});
</script>
<?php
}
// Ajax 処理の登録
add_action('wp_ajax_load_more_posts', 'my_load_more_posts');
add_action('wp_ajax_nopriv_load_more_posts', 'my_load_more_posts');
function my_load_more_posts() {
$paged = isset($_POST['paged']) ? absint($_POST['paged']) : 1;
$taxonomy = sanitize_key($_POST['taxonomy'] ?? '');
$term_id = absint($_POST['term_id'] ?? 0);
$list_type = sanitize_text_field($_POST['list_type'] ?? 'card');
if ( ! in_array( $taxonomy, ['category', 'post_tag'], true ) || ! term_exists( $term_id, $taxonomy ) ) {
wp_die('Invalid term', '', ['response' => 400]);
}
$list_type = in_array($list_type, ['card', 'list', 'list2', 'simple', 'big'], true) ? $list_type : 'card';
$args = [
'post_type' => 'post',
'posts_per_page' => max(1, (int) get_option('posts_per_page')),
'post_status' => 'publish',
'paged' => $paged,
'ignore_sticky_posts' => true,
];
if ( $taxonomy && $term_id ) {
$args['tax_query'] = [[
'taxonomy' => $taxonomy,
'field' => 'term_id',
'terms' => $term_id,
]];
}
$list_args = [
'type' => $list_type,
'paged' => $paged,
];
$query = new WP_Query($args);
$list_data = \SWELL_THEME\Parts\Post_List::get_list_data($list_args);
if ( $query->have_posts() ) :
while ( $query->have_posts() ) :
$query->the_post();
\SWELL_Theme::get_parts('parts/post_list/' . $list_data['parts_name'], $list_data['li_args']);
endwhile;
endif;
wp_reset_postdata();
wp_die();
}
add_action('wp_footer', 'my_load_more_inline_js');
function my_load_more_inline_js() {
?>
<script>
document.addEventListener('DOMContentLoaded', function () {
document.addEventListener('click', function (e) {
const btn = e.target.closest('#load-more');
if (!btn || btn.disabled) return;
e.preventDefault();
const list = document.querySelector('#main_content .p-archiveContent .p-postList, #main_content .p-termContent .p-postList');
if (!list) return;
let paged = parseInt(btn.dataset.paged, 10);
const maxPages = parseInt(btn.dataset.maxPages, 10);
const taxonomy = btn.dataset.taxonomy;
const termId = btn.dataset.termId;
const listType = btn.dataset.listType;
if (paged > maxPages) return;
btn.disabled = true;
btn.setAttribute('aria-busy', 'true');
const formData = new FormData();
formData.append('action', 'load_more_posts');
formData.append('paged', paged);
formData.append('taxonomy', taxonomy);
formData.append('term_id', termId);
formData.append('list_type', listType);
fetch('<?php echo esc_url( admin_url('admin-ajax.php') ); ?>', {
method: 'POST',
body: formData
})
.then(response => {
if (!response.ok) throw new Error('HTTP ' + response.status);
return response.text();
})
.then(html => {
const temp = document.createElement('div');
temp.innerHTML = html;
const items = [...temp.children].filter(el => el.matches('li.p-postList__item'));
if (!items.length) throw new Error('記事を取得できませんでした');
items.forEach(li => list.appendChild(li));
paged++;
btn.dataset.paged = paged;
if (paged > maxPages) btn.style.display = 'none';
})
.catch(err => {
console.error('読み込み失敗:', err);
btn.textContent = '再試行する(下のページ番号でも移動できます)';
}).finally(() => {
btn.disabled = false;
btn.removeAttribute('aria-busy');
});
});
});
</script>
<?php
}
あとは確認しましょう。
以上です。
取得成功と失敗時の操作を確認する
| 操作・条件 | 結果 |
|---|---|
| 初期表示からEnterで追加 | 3件から6件へ追加 |
| 次のEnterで最終ページへ | 合計7件となりボタンを非表示 |
| HTTP 503を返す通信失敗 | 3件のまま再試行ボタンを有効に戻す |
| 失敗後にページ番号2をEnter | 通常のページ遷移で4〜6番目の記事を表示 |
| 自動版のボタン付近へ移動 | 4〜6番目の記事を自動追加 |
追加記事のIDが重複しないことを確認しました。
2026年10月3日にタグ一覧の手動・自動追加と最終ページを追加確認しました。
記事の追加による途中の並び替えと、他のプラグインとの組み合わせは未検証です。
自動無限スクロールに変更する方法
上のmy_load_more_inline_js関数とそのadd_actionだけを、下の組み合わせへ置き換えます。同名関数を両方登録しないでください。ボタンは手動でも操作できます。
add_action('wp_footer', 'my_load_more_inline_js');
function my_load_more_inline_js() {
?>
<script>
document.addEventListener('DOMContentLoaded', function () {
const btn = document.getElementById('load-more');
const list = document.querySelector('#main_content .p-archiveContent .p-postList, #main_content .p-termContent .p-postList');
if (!btn || !list) return;
let paged = parseInt(btn.dataset.paged, 10);
const maxPages = parseInt(btn.dataset.maxPages, 10);
const taxonomy = btn.dataset.taxonomy;
const termId = btn.dataset.termId;
const listType = btn.dataset.listType;
// IntersectionObserverで監視
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting && paged <= maxPages) {
loadMorePosts();
}
});
});
observer.observe(btn); // 「もっと見る」ボタンを監視対象に
btn.addEventListener('click', loadMorePosts);
function loadMorePosts() {
if (btn.disabled || paged > maxPages) return;
btn.disabled = true;
btn.setAttribute('aria-busy', 'true');
const formData = new FormData();
formData.append('action', 'load_more_posts');
formData.append('paged', paged);
formData.append('taxonomy', taxonomy);
formData.append('term_id', termId);
formData.append('list_type', listType);
fetch('<?php echo esc_url( admin_url('admin-ajax.php') ); ?>', {
method: 'POST',
body: formData
})
.then(response => {
if (!response.ok) throw new Error('HTTP ' + response.status);
return response.text();
})
.then(html => {
const temp = document.createElement('div');
temp.innerHTML = html;
const items = [...temp.children].filter(el => el.matches('li.p-postList__item'));
if (!items.length) throw new Error('記事を取得できませんでした');
items.forEach(li => list.appendChild(li));
paged++;
btn.dataset.paged = paged;
if (paged > maxPages) {
btn.style.display = 'none';
observer.unobserve(btn);
}
})
.catch(err => {
console.error('読み込み失敗:', err);
btn.textContent = '再試行する(下のページ番号でも移動できます)';
observer.unobserve(btn);
}).finally(() => {
btn.disabled = false;
btn.removeAttribute('aria-busy');
});
}
});
</script>
<?php
}
追加取得の件数は「表示設定」の最大投稿数に合わせます。別のコードでメインクエリの件数・並び順・対象投稿を変えているサイトには、このまま適用しないでください。元へ戻すときは追加した関数とフックを削除(またはスニペットを無効化)し、表示件数を変更した場合は元の値に戻します。
2026年10月2日にWordPress 7.1.2 / SWELL 2.19.0の隔離カテゴリー一覧で、実際のHTTP取得とボタンのEnter操作を確認しました。
タグ一覧でボタンが出ない場合と最終ページの確認
| 条件 | 確認結果 |
|---|---|
| タグ一覧・手動 | Enterで3件→6件→7件、最後にボタンを非表示 |
| タグ一覧・自動 | ボタンの位置までスクロールして6件、さらに進んで7件 |
| カテゴリー一覧 | 追加操作で3件→6件 |
SWELL 2.19.0のカテゴリー・タグ一覧では、記事リストが.p-termContentの中に出力されます。
掲載コードは、ボタン作成・手動追加・自動追加の取得先として.p-archiveContentと.p-termContentの両方に対応します。
自動版は手動版のmy_load_more_inline_jsを置き換えて使い、二つの同名関数を同時に有効にしません。
2026年10月3日、隔離したWordPress 7.1.2 / SWELL 2.19.0で、通常投稿7件を同じタグに登録し、1ページの表示を3件にして確認しました。
手動・自動とも1〜7の記事が重複せずに並び、最終ページを追加した後はボタンが非表示になりました。
導入先でも記事件数、重複、最終ボタン、残したページ番号のリンクを確認します。
不具合が出た場合は追加したPHPを停止して通常のページ送りへ戻し、キャッシュ更新後の一覧で確認してください。
閲覧途中の投稿増減、最適化プラグイン併用、この修正後のスマホ操作は未検証です。




