LoginSignup
5
8

More than 5 years have passed since last update.

infinite scrollを高さの決まったoverflow:scrollなボックス内でゴリ押し実装した時の訳の分からないメモ

Last updated at Posted at 2014-09-26

※こんな感じにしたらとりあえず動いた!ってだけなんであまり参考にしないほうがいいです←
※あとマークダウンよくわかってないです←

[2014 09/30ソース修正]
より単純化しました。

[こういう感じのものができます]
スクリーンショット 2014-09-26 15.22.16.png
(コメント欄が無限スクロール)

[環境]
ruby 2.1.0p0
Rails 4.0.2
jquery使用
kaminari使用
bootstrap若干使用
turbolinks使用
jquery-turbolinksも使用

[事前準備]
・infinite scrollを読み込む
・バックエンド側でkaminariでURLにページ指定するとx件ずつjsonが返るようにする。

[例]
トークのやりとりを表示するとして、html.erb内にoverflow:scrollな表示領域を用意。
(事前準備で talks_path(:page => x)でxページ目のtalksがjsonで返る場合)

talk.html.erb
  <div id="talks">
    <div class="item">
      <!-- トーク文章1ページ分 -->
    </div>
    <div class="navigation">
       <%= link_to "next page?", talks_path(:page => 2), :remote => true, :class => "hidden" %>
    </div>
  </div>
talk.css.scss
  #talks{
    max-height:お好きなpx;
    overflow-y:scroll;
  }
talk.js
var clo_InfiniteScroll = function(){
  var pixelsDiffWinDoc = 0;
  var c_bufferPx = 40;//optionsのbufferPx定数
  $(function(){
    if($("#talks").length == 0){
      return;
    }
    var max_page = 1;

    pixelsDiffWinDoc = $(document).height() - $(window).height();//documentの縦幅とwindowの縦幅によってinfinity scrollのmathの計算がずれるため、それを調整する。

    $("#talks").infinitescroll({
        debug : false,
        loading:{
          finishedMsg: "last comment."
        },
        contentSelector : "#talks",
        itemSelector : "#talks > .item",
        navSelector  : "#talks > .navigation",
        nextSelector : "#talks > .navigation > a",
        dataType: "json",
        binder: $("#talks"),
        appendCallback: false,
        maxPage:999,//読み込む最大ページ数
        bufferPx: c_bufferPx,
        pixelsFromNavToBottom: -($("#talks")[0].scrollHeight - $("#talks").height()) + pixelsDiffWinDoc + c_bufferPx,
        errorCallback: function(){
          //適宜処理
        }
    }, function(json, opts) {
      if(json.length == 0){
        $("#talks").infinitescroll("update", { maxPage: max_page }); //全て読み込んだので最大ページを設定
        return;
      }
      $("#talks > .item:last").after('<div class="item"></div>');
      json.forEach(function(obj){
        $("#talks > .item:last").append(obj.html);
      });
      $("#talks").infinitescroll("update", {pixelsFromNavToBottom: -($("#talks")[0].scrollHeight - $("#talks").height()) + pixelsDiffWinDoc + c_bufferPx });
      max_page++;
    });
  });

  var timer = false;
  $(window).resize(function() {
    if (timer !== false) {
        clearTimeout(timer);
    }
    timer = setTimeout(function() {
      if( $("#talks").length == 0 ){
        return;
      }
      pixelsDiffWinDoc = $(document).height() - $(window).height();//documentの縦幅とwindowの縦幅の差を再計算
      $("#talks").infinitescroll("update", {pixelsFromNavToBottom: -($("#talks")[0].scrollHeight - $("#talks").height()) + pixelsDiffWinDoc + c_bufferPx });//infinity scroll更新
    }, 200);
  });
}();

infinite scrollのoptions設定の方はまぁ公式のドキュメント参照して下さい自分もあんまよくわかってない。

で、なにやってるかっていうと、
どうやらinfinite scroll内で、binderに指定した要素をスクロールするたびにdocumentの縦幅、windowの縦幅、binder要素のscrollTopからmathとかいう値が計算されて、その値が (pixelsFromNavToBottom + bufferPx) 以下になる度にinfinitescrollメソッドの第二引数に指定したfunctionが呼ばれる仕組みらしい。(違ってたらごめん)

 -($("#talks")[0].scrollHeight - $("#talks").height()) + pixelsDiffWinDoc + c_bufferPx
で次のページを読み込むべきスクロールの距離が得られるので、ページ読み込み時に都度この値をpixelsFromNavToBottomに設定してやれば動作しました。ただbootstrap等でoverflow:scroll内部のコンテンツの高さがウィンドウサイズにより変動する場合、この値は自動で書き換わらないので再計算してアップデートしてやる必要があります。(window.resize部)
turbolinkはbinderが$(window)ではないのでイベントが2重で発生するようなことは今のところ起きていません。

上の例だとbufferPxには標準の挙動の場合、およそ40以上を指定しないと読み込まない可能性高いです。
次ページ分のレコード読み込み中に一瞬だけ表示されるロード演出分の高さを含めた分のscrollHeightで計算しちゃうのが原因。

5
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
8