WordPress に Google 風の無限スクロールを実装
動作
2018 年 4 月に、Google がモバイル検索結果で無限スクロールを導入しました。
Google がモバイル検索結果で導入した無限スクロールは、「もっと見る」ボタンのタップで、ページを移動せず検索結果の続きが表示されます。
We’ve launched a new “More results” button that makes it easier & faster to get more search results on mobile. Now additional listings load below the ones already being viewed, rather than the old “Next” button that would load an entirely new page… pic.twitter.com/A4ZMXFlwd0
— Google SearchLiaison (@searchliaison) April 11, 2018
この Google 検索に似た無限スクロールを WordPress に実装する方法をご紹介します。
まずは、実際の動作をご覧ください。Google 検索と同じように、「もっと見る」ボタンのクリック・タップで次のページが読み込まれます。
Infinite Scroll を使用
無限スクロールを導入する際は、下記の 2 つを考慮しないといけません。
- クローラーに配慮する
- ユーザーの使い勝手に配慮する
クローラーは無限スクロールが苦手です。そのため、検索エンジンとの相性を考慮した無限スクロールのベストプラクティスに沿う必要があります。
また、自動的に無限スクロールが始まると、サイトの使い勝手が悪くなる恐れがあります。「フッターを見たいのに、見られない!」と私は何度も思ったことがあります。
この 2 つとも考慮できるのが、JavaScript ライブラリの Infinite Scroll です。設置方法が簡単な魅力もあるため、今回は Infinite Scroll を使用します。
尚、今回は紹介しませんが、Infinite Scroll であれば「もっと見る」ボタンをクリック・タップしない自動的な無限スクロールにも対応できます。
Infinite Scroll の読み込み
We’ve launched a new “More results” button that makes it easier & faster to get more search results on mobile. Now additional listings load below the ones already being viewed, rather than the old “Next” button that would load an entirely new page… pic.twitter.com/A4ZMXFlwd0
— Google SearchLiaison (@searchliaison) April 11, 2018
最初にすることは、Infinite Scroll の設置と読み込みです。
使用中のテーマフォルダの直下に、infinite-scroll.js ファイルを作成します。
作成した infinite-scroll.js ファイルに、infinite-scroll.pkgd.min.js をそのまま全てコピペをします。
あとは作成した infinite-scroll.js をサイトで読み込みます。下記のコードを使い、</body>
タグ直前で読み込みます(<head>
タグ内でも可)。
<script src="<?php echo get_stylesheet_directory_uri(); ?>/infinite-scroll.js"></script>
上記のコードは、子テーマの使用を想定しています。
「もっと見る」ボタンを作成
次にすることは、無限スクロールを開始する「もっと見る」ボタンの作成です。
テーマフォルダの直下に、infinite-scroll.php ファイルを作成します。作成した infinite-scroll.php ファイルに、下記のコードを追加します。
<?php
// 現在のページ数
global $paged;
if ( empty( $paged ) ) $paged = 1;
// 総ページ数
global $wp_query;
$pages = $wp_query -> max_num_pages;
if ( ! $pages ) {
$pages = 1;
}
// ページが1ページしかない場合は出力しない・最後のページでも出力しない
if ( $pages != 1 && $paged < $pages ) {
echo '
<button id="more-button" type="button">もっと見る</button>
<div class="scroller-status">
<div class="infinite-scroll-request"></div>
<p class="infinite-scroll-last">これ以上は記事がありません</p>
<p class="infinite-scroll-error">読み込むページがありません</p>
</div>
';
}
ページが 1 ページしかない場合は、ボタンは表示しないようにしています。また、最後のページでもボタンは表示されません。読み込むページがない時は、ボタンは不要です。
scroller-status
の class が付いている <div>
は、Infinite Scroll のオプションの status を使うためのものです。status の使用により、最後の記事の下に「これ以上は記事がありません」と表示されます。
読み込むページがない場合は、「読み込むページがありません」と表示されます。
テンプレートにボタンを設置
作成した infinite-scroll.php ファイルを、記事一覧を呼び出しているテンプレートのお好きな場所に設置します。
設置するためのコードは、下記のとおりです。
<?php get_template_part( 'infinite-scroll' ); ?>
テーマによって、記事一覧を呼び出しているテンプレートは違います。Simplicity であれば list.php 。Cocoon であれば tmp/list.php です。
最適なボタンの設置場所はサイトによって異なりますが、おすすめはページネーションの上です。
JavaScript で設定
次は Infinite Scroll の設定です。下記の JavaScript を追加します。コードを追加するファイルは、テーマが用意している JavaScript 用のファイル、または最初に作成した infinite-scroll.js ファイルのどちらでも構いません。
var infScroll = new InfiniteScroll('#list', {
append: '.entry',
path: '.next a',
hideNav: '.pagination',
button: '#more-button',
scrollThreshold: false,
status: '.scroller-status',
history: 'push',
});
1 行ずつ解説します。
まず 1 行目の #list
は、記事一覧を囲む要素を指定します。例えば、記事一覧の HTML が下記のものだとします。
<div id="list">
<article class="entry"></article>
<article class="entry"></article>
<article class="entry"></article>
...
</div>
各記事の <article>
を <div id="list"></div>
が囲んでいます。この囲んでいる要素の id または class を指定します。
append: '.entry'
の .entry
は、各記事(上記の HTML では <article>
)の class を指定します。この append に指定した要素が、無限スクロールで読み込まれたページに追加されていきます。
path: '.next a'
の .next a
は、次のページへの URL を指定します。URL は、ページネーションから拾います。例えば、ページネーションの HTML が下記のものだとします。
<ul class="pagination">
<li><a href="最初のページのURL"></a></li>
<li><a href="現在のページの前のURL"></a></li>
<li>現在のページ数</li>
<li><a href="現在のページの次のURL"></a></li>
<li><a href="現在のページの次の次のURL"></a></li>
...
<!-- この a タグを指定します -->
<li class="next"><a href="次のページのURL"></a></li>
<li><a href="最後のページのURL"></a></li>
</ul>
.next a
が、次のページの URL です。たいてい「次へ」とか「 > 」で表示されています。この <a>
タグの id または class を指定します。
それ以外の部分は、変更する必要はありません。
Infinite Scroll の詳細な設定は、Options にて公式の説明をご覧いただけます。
Safari のバグへの対策
iOS(Safari)でバグがあることを教えていただきました。
各記事に画像があり、その画像に srcset 属性があると、無限スクロールで読み込まれたページで画像が表示されないというものです。
GitHub の Safari not displaying loaded srcset images にて、開発者さんが問題の原因と解決方法をお書きになっています。
Infinite Scroll の設定の JavaScript の後に JavaScript を追加すれば、この問題を解決できます。
jQuery で書く場合は、以下のコードを追加します。2 行目の #list
には、記事一覧を囲む要素を指定します。
jQuery(function ($) {
$('#list').on(
'append.infiniteScroll',
function (event, response, path, items) {
$(items)
.find('img[srcset]')
.each(function (i, img) {
img.outerHTML = img.outerHTML;
});
}
);
});
Vanilla JS(素の JavaScript)で書く場合は、以下のコードを追加します。1 行目の .entry
には、各記事の class を指定します。ただし、こちらのコードは実際に検証していません。
var infiniteItem = document.querySelectorAll('.entry');
infScroll.on('append', function (response, path, items) {
for (var i = 0; i < items.length; i++) {
reloadSrcsetImgs(infiniteItem[i]);
}
});
function reloadSrcsetImgs(infiniteItem) {
var imgs = infiniteItem.querySelectorAll('img[srcset]');
for (var i = 0; i < imgs.length; i++) {
var img = imgs[i];
img.outerHTML = img.outerHTML;
}
}
CSS で装飾
あとは「もっと見る」ボタンなどの CSS を指定すれば終わりです。
CSS の一例は下記のとおり。サイトによって、CSS はだいぶ違うはずです。
#more-button {
background: #eee;
height: 56px;
margin: 40px 0 0;
width: 100%;
}
.scroller-status {
display: none;
margin-top: 40px;
}
/* ローディングアイコン */
.infinite-scroll-request {
animation: scroll-request 1.1s infinite linear;
border: 4px solid #00b8d4;
border-left: 4px solid #fff;
border-radius: 50%;
height: 48px;
margin: auto;
width: 48px;
}
@keyframes scroll-request {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.infinite-scroll-last,
.infinite-scroll-error {
color: #757575;
text-align: center;
}
ローディングアイコンを指定している部分があります。ローディングアイコンは、「もっと見る」ボタンをクリック・タップした後に表示されます。
ローディングアイコンの CSS は、Single Element CSS Spinners をご参考にするといいかもしれません。