無限スクロール、いいですよね。
ところが巷に配布されているプラグインの多くは、ブラウザのバックボタンで戻ったら最初から読み込み直しでヴァー、ということがあってユーザビリティ上よろしくないのです。
Google先生が対応策としてサンプルのJavascriptをつくってくれたので、いろんな案件で使えるようにちょっと手を入れてみました。
元にしたのはこちらのサンプルです。
http://scrollsample.appspot.com/items
解説は下記ブログ記事をご覧ください。
Googleが推奨するSEOに適した無限スクロールの構成方法
- あらかじめページを分割しておく。いわゆるページネーション
- 分割されたページのうち1つを表示(1ページ目でなくても見られるようにする)
- その上下に前のページ、次のページをAjaxで読み込んでいく
- 今表示している部分が何ページ目に相当するかに合わせて、URLを動的に書き換える
自分なりに書き換えてみたソースは下記です。jQueryを読み込んだ後に実行してください。
$(function (){
//infinite scroll
var prev_data_selector = '.prev.page-numbers'; //「前へ」リンクのaタグのセレクタ
var next_data_selector = '.next.page-numbers'; //「次へ」リンクのaタグのセレクタ
var itemWrapperSelector = '.list_infinite'; //無限ローディングのwrapperとなるタグのセレクタ
var itemWrapper = $(itemWrapperSelector);
var itemInnerSelector = '.list_infinite_item'; //無限ローディングの本体(本文)となるタグのセレクタ
var itemPagerSelector = '.pagination'; //ページャーのセレクタ
var itemPager = $(itemPagerSelector);
var prev_data_url;
var next_data_url;
var next_data_cache;
var prev_data_cache;
var last_scroll = 0;
var is_loading = 0;
if(itemWrapper[0]) {
prev_data_url = $(prev_data_selector).attr('href');
next_data_url = $(next_data_selector).attr('href');
fadeInItem(true);
initPaginator();
loadPrevious();
// if we have enough room, load the next batch
if ($(window).height()>itemWrapper.height()) {
if (next_data_url!== '') {
loadFollowing();
}
}
}
function initPaginator() {
$(document).off( 'scroll');
$(document).on( 'scroll', function(){
// handle scroll events to update content
var scroll_pos = $(window).scrollTop();
if (scroll_pos >= 0.9*($(document).height() - $(window).height())) {
if (is_loading===0) {
loadFollowing();
}
}
if (scroll_pos <= 0) {
if (is_loading===0) {
loadPrevious();
}
}
// Adjust the URL based on the top item shown
// for reasonable amounts of items
if (Math.abs(scroll_pos - last_scroll)>$(window).height()*0.1) {
last_scroll = scroll_pos;
$(itemInnerSelector).each(function() {
if (mostlyVisible(this)) {
history.replaceState(null, null, $(this).attr('data-url'));
return(false);
}
});
}
});
}
function loadFollowing() {
if (next_data_url){
is_loading = 1; // note: this will break when the server doesn't respond
itemPager.hide();
if (next_data_cache) {
showFollowing(next_data_cache);
is_loading = 0;
} else {
$.ajax({
url: next_data_url,
type: 'get',
dataType: 'html',
}).success(function(data){
showFollowing(data);
is_loading = 0;
});
}
}
}
function showFollowing(data) {
var out_html = $($.parseHTML(data));
itemWrapper.append(out_html.find(itemWrapperSelector).filter(itemWrapperSelector)[0].innerHTML);
initPaginator();
next_data_url = out_html.find(next_data_selector).filter(next_data_selector).attr('href');
next_data_cache = false;
$.ajax({
url: next_data_url,
type: 'get',
dataType: 'html',
}).success(function(preview_data){
next_data_cache = preview_data;
});
}
function loadPrevious() {
if (prev_data_url){
is_loading = 1; // note: this will break when the server doesn't respond
itemPager.hide();
if (prev_data_cache) {
showPrevious(prev_data_cache);
is_loading = 0;
} else {
$.ajax({
url: prev_data_url,
type: 'get',
dataType: 'html',
}).success(function(data){
showPrevious(data);
is_loading = 0;
});
}
}
}
function showPrevious(data) {
var out_html = $($.parseHTML(data));
itemWrapper.prepend(out_html.find(itemWrapperSelector).filter(itemWrapperSelector)[0].innerHTML);
initPaginator();
var item_height = $(itemInnerSelector + ':first').height();
window.scrollTo(0, $(window).scrollTop()+item_height); // adjust scroll
prev_data_url = out_html.find(prev_data_selector).filter(prev_data_selector).attr('href');
prev_data_cache = false;
$.ajax({
url: prev_data_url,
type: 'get',
dataType: 'html',
}).success(function(preview_data){
prev_data_cache = preview_data;
});
}
function mostlyVisible(element) {
var scroll_pos = $(window).scrollTop();
var window_height = $(window).height();
var el_top = $(element).offset().top;
var el_height = $(element).height();
var el_bottom = el_top + el_height;
return ((el_bottom - el_height*0.25 > scroll_pos) &&
(el_top < (scroll_pos+0.5*window_height)));
}
});