無限スクロール、いいですよね。
ところが巷に配布されているプラグインの多くは、ブラウザのバックボタンで戻ったら最初から読み込み直しでヴァー、ということがあってユーザビリティ上よろしくないのです。
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))); } });