無限スクロール(Infinite scroll)は非常に一般的になった機能ですが、覚えておくと役立ちます。例えば、TwitterやFacebookなどもそうですが、ページ割りの仕組みがないWebサイトの場合です。また、サーチエンジンにも無限スクロールは有効です。無限スクロールにすれば、欲しいリンクが見つかるまでスクロールするので、ページごとで検索するよりサイト滞在時間が長くなります。もしプロジェクトで無限スクロールを使う必要がある場合は、以下に紹介する6つのデモをぜひ参考にしてみてください。
注意点として、6つめの例を除いてデモはすべてjQueryで書かれていて、いくつかはjQueryプラグインを使用しています。6つめの例は簡単にvanilla JSに置き換えられます。
1.無限スクロールとグリッド
このデモはInfinite ScrollとjQuery Masonryプラグインを使います。Masonryプラグインは可変グリッドレイアウトを取得するのに便利です。Paul IrishによるInfinite Scrollプラグインは既存のページを読み込むのに優れており、SEO対策にも有効です。このプラグインはpage2.htmlやpage3.htmlなどの静的ページの読み込みにも使えますが、page.php?p=2やpage.php?p=3などの動的ページも扱えます。ただし、URL内に随時増えていくページ番号が必要になるため、もしpage.php?data=xxxのようなページがある場合、このプラグインは不向きです。
【HTML】
<div class="grid">
<div class="grid-item grid-item-2">
<p>content</p>
</div>
…
</div>
<!-- The next page which content will be loaded when scrolled -->
<nav id="pagination">
<p><a href="page-2.html">Page 2</a></p>
</nav>
【jQuery】
$(document).ready(function() {
var grid = $('.grid');
grid.masonry({
itemSelector: '.grid-item',
columnWidth: 200
});
grid.infinitescroll({
// Pagination element that will be hidden
navSelector: '#pagination',
// Next page link
nextSelector: '#pagination p a',
// Selector of items to retrieve
itemSelector: '.grid-item',
// Loading message
loadingText: 'Loading new items…'
},
// Function called once the elements are retrieved
function(new_elts) {
var elts = $(new_elts).css('opacity', 0);
elts.animate({opacity: 1});
grid.masonry('appended', elts);
});
});
2. ブログ投稿経由の無限スクロール
このデモは無限スクロールのために、プラグインもライブラリーも使いません。ページ終わりになるたびに、該当するHTMLコードをそのまま実行するPHPスクリプトによって生成された新しいコードを読み込みます。デモではコンテンツの最後までたどり着きませんが、表示する投稿がなくなった時点で空の文字列を呼び出して終わります。今回はTwitterのように、ページ最後に読み込み中の画面を表示します。
注意点:以下のデモではCodePen内でPHPスクリプトが使えないため、新しい投稿はJavaScript機能で生成されています。
【HTML】
<ul id="posts">
<li>
<article>content</article>
</li>
…
</ul>
<p id="loading">
<img src="images/loading.gif" alt="Loading…" />
</p>
【jQuery】
$(document).ready(function() {
var win = $(window);
// Each time the user scrolls
win.scroll(function() {
// End of the document reached?
if ($(document).height() - win.height() == win.scrollTop()) {
$('#loading').show();
$.ajax({
url: 'get-post.php',
dataType: 'html',
success: function(html) {
$('#posts').append(html);
$('#loading').hide();
}
});
}
});
});
3. 画像経由の無限スクロール
このデモは無限スクロール中の画像を読み込みます。これも最後までたどり着くことはありません。画面下から指定したピクセル数の画像を読み込む仕掛けがカスタマイズできるjQuery Endless Scrollプラグインを使用しています。デモでは同じ画像を複製してリスト最後に挿入することを繰り返しますが、スクリプトを違うソースから画像を読み込むようカスタマイズすることは比較的簡単です。
【HTML】
<ul id="images">
<li>
<a href="https://www.pexels.com/photo/mist-misty-fog-foggy-7919/">
<img src="https://pexels.imgix.net/photos/7919/pexels-photo.jpg?fit=crop&w=640&h=480" alt="" />
</a>
</li>
…
</ul>
【jQuery】
$(document).ready(function() {
$(window).endlessScroll({
inflowPixels: 300,
callback: function() {
var $img = $('#images li:nth-last-child(5)').clone();
$('#images').append($img);
}
});
});
4. 無限番号リスト
このデモでは、デモ3と同じプラグインを使いますが、リストに独自の垂直スクロールバーを使って無限スクロールを適用した点が違います。下にスクロールしていくと、数字がリストアイテムとして付け足されていく仕組みです。
【HTML】
<ul id="numbers">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
…
</ul>
【jQuery】
$(document).ready(function() {
var offset = $('#numbers li').length;
$('#numbers').endlessScroll({
fireOnce: false,
fireDelay: false,
loader: '',
insertAfter: '#numbers li:last',
content: function(i) {
return '<li>' + (i + offset) + '</li>';
}
});
});
5. 無限スプレッドシート
このデモでは、垂直だけなく水平方向で最終ページにきたときにデモ2と同じテクニックを使います。ページが終わるたびに新しい列やカラムをテーブルに追加します。これはスプレッドシートのアプリを作りたいときに書くスクリプトと同じです。
注意点:セルがすべて空になっていることを忘れずにチェック。列とカラムインデックスはCSSカウンターで生成されているので、自分でカウントする必要はありません。
【HTML】
<table id="spreadsheet">
<tr>
<td></td>
<th></th>
…
</tr>
<tr>
<th></th>
<td></td>
…
</tr>
…
</table>
【jQuery】
$(document).ready(function() {
var win = $(window);
var doc = $(document);
// Each time the user scrolls
win.scroll(function() {
// Vertical end reached?
if (doc.height() - win.height() == win.scrollTop()) {
// New row
var tr = $('<tr />').append($('<th />')).appendTo($('#spreadsheet'));
// Current number of columns to create
var n_cols = $('#spreadsheet tr:first-child th').length;
for (var i = 0; i < n_cols; ++i)
tr.append($('<td />'));
}
// Horizontal end reached?
if (doc.width() - win.width() == win.scrollLeft()) {
// New column in the heading row
$('#spreadsheet tr:first-child').append($('<th />'));
// New column in each row
$('#spreadsheet tr:not(:first-child)').each(function() {
$(this).append($('<td />'));
});
}
});
});
6. 無限スクロール+ページ割り
無限スクロールにはいくつか欠点があります。きちんと実装されないとユーザーエクスペリエンスがダメになるという点です。無限スクロールをしばらく続けると、自分が見ている画面がどこか分からなくなる可能性があります。これが従来のページ割りではなかった点です。しかし、ページ割りは無限スクロールが必要ない分、クリックしてページを進める必要があります。
上記の2点から Tim Severienはあるアイデアを思いつきました。もし、無限スクロールとページ割りの両方のメリットをいかしたらどうなるか? ということです。その結果が最後に紹介するこのデモです。
最初のページが表示され、スクロールしてページ下までたどり着くと、自動で新しいページが読み込まれます。これは無限スクロールではおなじみの動作です。でも少し違うのは、新しいページが画面下の固定されたリストから表示されるという点です。
最初は隠れているのですが、新しいページがその番号と共に読み込まれるとリストが埋まります。こうして、2番目のページに遷移したいときは、ただその番号をクリックするだけで目的のページへ移れるのです。
【HTML】
<div class="article-list" id="article-list"></div>
<ul class="article-list__pagination article-list__pagination--inactive" id="article-list-pagination"></ul>
【JavaScript】
注意点:このコードはES6を使っていますが、ES5へも簡単に変換できます。
function getPageId(n) {
return 'article-page-' + n;
}
function getDocumentHeight() {
const body = document.body;
const html = document.documentElement;
return Math.max(
body.scrollHeight, body.offsetHeight,
html.clientHeight, html.scrollHeight, html.offsetHeight
);
};
function getScrollTop() {
return (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
}
function getArticleImage() {
const hash = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
const image = new Image;
image.className = 'article-list__item__image article-list__item__image--loading';
image.src = 'http://api.adorable.io/avatars/250/' + hash;
image.onload = function() {
image.classList.remove('article-list__item__image--loading');
};
return image;
}
function getArticle() {
const articleImage = getArticleImage();
const article = document.createElement('article');
article.className = 'article-list__item';
article.appendChild(articleImage);
return article;
}
function getArticlePage(page, articlesPerPage = 16) {
const pageElement = document.createElement('div');
pageElement.id = getPageId(page);
pageElement.className = 'article-list__page';
while (articlesPerPage--) {
pageElement.appendChild(getArticle());
}
return pageElement;
}
function addPaginationPage(page) {
const pageLink = document.createElement('a');
pageLink.href = '#' + getPageId(page);
pageLink.innerHTML = page;
const listItem = document.createElement('li');
listItem.className = 'article-list__pagination__item';
listItem.appendChild(pageLink);
articleListPagination.appendChild(listItem);
if (page === 2) {
articleListPagination.classList.remove('article-list__pagination--inactive');
}
}
function fetchPage(page) {
articleList.appendChild(getArticlePage(page));
}
function addPage(page) {
fetchPage(page);
addPaginationPage(page);
}
const articleList = document.getElementById('article-list');
const articleListPagination = document.getElementById('article-list-pagination');
let page = 0;
addPage(++page);
window.onscroll = function() {
if (getScrollTop() < getDocumentHeight() - window.innerHeight) return;
addPage(++page);
};
まとめ
今回、無限スクロールを埋め込む6つの例を紹介しました。どのようなプロジェクトにせよ、今回紹介したデモのうち、少なくとも1つは使えるでしょう。