11
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【jQuery, kaminari】無限スクロールと逆スクロール機能の実装

Last updated at Posted at 2020-02-11

0. これは何?

LINEライクなチャットアプリ作成の一端。
一部jQueryのライブラリやRailsのgem kaminariを使用。

  1. 無限スクロールの実装
  2. 逆スクロールの実装

に分けて解説。

1. 無限スクロールの実装

完成形ソースコード

####js側

infinite_scroll.js
$(document).on("turbolinks:load", function() {
  if ($("nav.pagination a[rel=next]").length){
    $(".user_list").infiniteScroll({
      path: "nav.pagination a[rel=next]",
      append: ".user_list ul",
      elementScroll: true,
      history: true,
      prefill: false,
      status: ".page-load-status",
      hideNav: ".pagination"
    })

####view側

<div class="user_list">
  <ul>
    <% @rooms.each do |room| %>
      <li>
        <%= link_to (room) do %>
          <div class="icon">
            <img src="<%= room.user.picture_url %>" alt="<%= room.user.display_name %>">
          </div>
          <p data-id="<%= room.id %>"><%= room.user.display_name %></p>
          <span class="content"><%= room.last_message %></span>
          <i class="alert"><%= room.unread_count %></i>
          <time><%= room.updated_at.to_s(:datetime_jp) %></time>
        <% end %>
        <div class="pin">
          <%= link_to "", pin_room_path(room.id), method: :post, remote: true, class: room.pin ? 'icon-star2' : 'icon-star' %>
        </div>
      </li>
    <% end %>
  </ul>
  <%= paginate @rooms, param_name: :rooms_page %>
  <div class="page-load-status">
    <p class="infinite-scroll-request">
    読み込み中...
    </p>
  </div>
</div>

解説

turbolinks

$(document).on("turbolinks:load", function() {})

jqueryを使うと初回ページ読み込み&リロードで動くが、ページ遷移して該当ページに戻ってくるとjqueryが動かない。
デフォルトgemであるturbolinks(ajaxとajaxとhistoryAPIを使ってページ遷移しやすくするもの)が動作しているため、そもそも$(document).ready()が動かない

DOM要素の存在確認

if ($("nav.pagination a[rel=next]").length){}

Infinite Scroll

$(".user_list").infiniteScroll({  // コンテナ(スコープとなるセレクタ)に対し無限スクロールを実行
  path: "nav.pagination a[rel=next]",  // 次ページへのページネーションリンクを指定
  append: ".user_list ul",  // コンテナに追加する要素の指定 ※コンテテナの指定とappendする要素の指定が上手くいかないとレイアウトが崩れるなどの問題が起こる
  elementScroll: true,  // 無限スクロールを適用する範囲の指定。trueだとコンテナを無限スクロール範囲とする。デフォルトでは`window`全体が適用範囲となる。
  history: true,  // 読み込むたびにURLを書き換えるかどうかの指定
  prefill: false,  // 一番下までスクロールする前に読み込むかどうかの指定
  status: ".page-load-status",  // 読み込み中や読み込み終了時に表示するものを指定
  hideNav: ".pagination"  // ページネーションリンクを表示するかどうかを指定(display: noneと同じ)
})

補足

無限スクロールを実装するにあたって、HTML・CSSのレイアウトが正確であることは確認する必要がある。

また、infinite scrollの初期仕様上、スクロールして最下部まで行かないと次のロードが始まりません。
つまり、Windowの縦幅が広く、最初の状態よりも長い幅になっているとinfinite scrollが発生しないかもしれないです。
Windowサイズを縦に小さくして確認してみてください。

Ruby on Rails、 Infinite Scrollとkaminariを使用して無限スクロールの実装について

2. 逆スクロールの実装

完成形ソースコード

####js側

reverse_scroll.js
$(document).on("turbolinks:load", function() {
    var i = 2;
    $(".chat").scrollTop(1);
    $(".chat").scroll(function() {
      if (($(".chat").scrollTop() == 0) && (i <= parseInt($(".message_view").find("nav.pagination span.last a").prop("search").match(/[0-9]/), 10))){
        $.ajax({
          url: $(".message_view").find("nav.pagination a[rel=next]").prop("search").replace(/[0-9]/, i)
        }).done(function(data) {
          $(".chat ul").prepend($(data).find(".chat ul").html());
          $(".chat").scrollTop($(".chat").first().height());
          i++;
        })
      }
    })
  }
})

####view側
順スクロールと同じ。

解説

大まかな基本方針はだいたいこんな感じ。

  1. 表示されているメッセージページを上までスクロールする。
  2. 表示されているメッセージの上に過去のメッセージを追加する。
  3. Ajax通信を使用。

turbolinks

$(document).on("turbolinks:load", function() {})

jqueryを使うと初回ページ読み込み&リロードで動くが、ページ遷移して該当ページに戻ってくるとjqueryが動かない。
デフォルトgemであるturbolinks(ajaxとajaxとhistoryAPIを使ってページ遷移しやすくするもの)が動作しているため、そもそも$(document).ready()が動かない

1. スクロールウィンドウでの初期位置の指定

$(".chat").scrollTop(1);

.scrollTop( ):
指定した要素のスクロール位置(Y座標)を取得。引数を設定した場合はjQueryオブジェクトで指定した、要素のスクロール位置を設定

2. スクロール時の処理の設定

$(".chat").scroll(function() {})

.scroll( ):
スクロールした時に引数のfunctionを実行する。ブラウザイベントの関数。

3. スクロール時に処理を実行する条件の指定

 if (($(".chat").scrollTop() == 0) && (i <= parseInt($(".message_view").find("nav.pagination span.last a").prop("search").match(/[0-9]/), 10)))
  • $(".chat").scrollTop() == 0: 指定した要素のスクロール位置が0かどうか。

  • parseInt(): 第1引数の文字列をパースし、第2引数に与えられた基数にもとづく整数を返す。

  • $(".message_view").find("nav.pagination span.last a"): ページネーションリンクの最後のページを指すリンクを指定。

    • .find( ): jQueryオブジェクトで指定した要素の子孫要素の中で、引数で指定したセレクタ(jQueryオブジェクト、DOM要素)にマッチする要素を選択。
  • .prop("search"): リンクのURLパラメータ(eg.?page=2)を取得

    • .prop( ): 引数にプロパティを指定することでプロパティの値を取得する。指定したプロパティがない場合はundefinedを返す。第2引数に値や処理を設定することでプロパティの値を設定、変更する。

      JavaScript のオブジェクトは、自身に関連付けられたプロパティを持ちます。オブジェクトのプロパティは、オブジェクトに関連付けられている変数と捉えることができます。オブジェクトのプロパティは、オブジェクトに属するものという点を除けば、基本的に通常の JavaScript 変数と同じようなものです。オブジェクトのプロパティは、オブジェクトの特性を定義します。オブジェクトのプロパティには、単純なドット表記でアクセスします。

      object_dot_property.js
      var myCar = new Object();
      myCar.make = 'Ford';
      myCar.model = 'Mustang';
      myCar.year = 1969;
      
  • .match(/[0-9]/), 10): 正規表現で数字にマッチするもの(=ページ番号(e.g. 2))を取得。

    • .match(): 引数に指定した正規表現に対する文字列オブジェクトへのマッチングの際に、そのマッチの結果を得るために使われる。

4. Ajaxによる非同期通信で次ページのHTML要素を取得

$.ajax({
  url: $(".message_view").find("nav.pagination a[rel=next]").prop("search").replace(/[0-9]/, i)
}).done(function(data) {
  $(".chat ul").prepend($(data).find(".chat ul").html());
  $(".chat").scrollTop($(".chat").first().height());
  i++;
})
  • .load(): jQueryオブジェクトで指定した要素にサーバーから受信した情報をhtmlコンテンツとして読み込む。第2引数にobjectオブジェクトを設定することで、サーバーにデータを送信することができる。第3引数にfunctionを設定することで、読み込みが完了したら処理を実行することができる。

  • jQuery.get(): jQuery.ajaxメソッドを簡単に利用できるようにしたメソッド。loadメソッドはセレクタで指定した要素に強制的に読み込まれたデータを表示するが、データによってはな表示せずに内部的に利用したい場合があるのでその場合に利用する。引数に設定する内容はloadメソッドと似ている。

  • jQuery.ajax( ): jQuery.get()メソッドの拡張版。第1引数にurlを設定できる。

11
12
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
11
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?