6
4

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.

Rails 無限スクロール(gem、ライブラリ無し)

Last updated at Posted at 2021-09-03

####はじめに
「Rails 無限スクロール」と検索するとたくさんの記事が出てきます。kaminariやjsライブラリを利用して作成している記事をたくさん見かけますが、今回は自分の学習のためにもgemやライブラリなしで実装をしていきたいと思います。(デフォルトのgemであるjbuilderと記法を変更するだけのjqueryは利用します。)

####完成イメージ
ezgif.com-gif-maker.gif

####前提条件(無限スクロール実装前)
簡単な例を作成して記事を書いていきます。下記の条件でbooksテーブルに100件レコードを作成しました。
erd

app/controllers/books_controller.rb
class BooksController < ApplicationController

  def index
    @books = Book.all
    @book = Book.new
  end

  def create
    @book = Book.new(book_params)
    @book.user = current_user
    @book.save
    @book_new = Book.new
  end

  private
  def book_params
    params.require(:book).permit(:title, :body)
  end
end
app/views/books/index.html.erb
<h4>新規投稿</h4>
<div class='scroll__new'>
  <%= render 'new', book: @book %>
</div>

<h4>投稿一覧</h4>
<table class='scroll__table'>
  <thead>
    <tr>
      <th class='scroll__th'>タイトル</th>
      <th class='scroll__th'>内容</th>
    </tr>
  </thead>
  <tbody class='scroll__tbody'>
    <% @books.each do |book| %>
      <tr>
        <td class='scroll__td'><%= book.title %></td>
        <td class='scroll__td'><%= book.body %></td>
      </tr>
    <% end %>
  </tbody>
</table>
app/assets/stylesheets/scroll.scss
.scroll {
  &__table {
    border-collapse: collapse;
  }

  &__tbody {
    height: 500px;
    display: block;
    overflow-y: scroll;
  }

  &__th,
  &__td {
    height: 40px;
    width: 100px;
    display: inline-block;
    text-align: center;
    line-height: 40px;
    border: 1px solid;
  }
}

scssは最低限記述しています。下記2点は機能を満たすために最低限必要です。

  1. tbodyのstyle
  2. tdのheight
    tdのheightについては、scroll出来ればいいので次の条件を満たせば問題ありません。
    tdのheight * 初期表示レコード数 > tbodyのheight

####無限スクロール機能の作成
では、現在全てのレコードが表示されているbooks一覧を15件ずつ取得し表示するように変更していきたいと思います。
#####処理の流れ

  1. #indexにて最新15件を取得し表示
  2. scroll範囲最上部までscrollするとイベント発火
  3. #scrollにて追加のレコードを取得
  4. jbuilderにてjson形式に変換しデータを返却する
  5. 返ってきたデータを元にjs側で処理

#####実装
######1. #indexにて最新15件を取得し表示

app/controllers/books_controller.rb
def index
  @books = Book.all.order(id: 'DESC').limit(15).offset(0)
  @book = Book.new
end

limitとoffsetについてはこちらを参照してください。今回はこのoffsetを調整することで、追加のレコードを読み込んでいきます。
また、order(id: 'DESC')を指定したことでview側での並び順も逆順となっていますので、viewのeach部分を変更します。

app/views/books/index.html.erb
<% @books.each do |book| %>
<%# 上の記述を下のように変更します %>
<% @books.reverse_each do |book| %>

######2.scroll範囲最上部までscrollするとイベント発火

app/assets/javascripts/scroll.js
$(document).on('turbolinks:load', function() {
  // load時最下部までscroll
  const target = $('.scroll__tbody');
  target.scrollTop(600); //tdのheight * 15件

  target.scroll(function(){
    if (target.scrollTop() == 0){
      const offset = target.children().length;
      $.ajax({
        type: 'GET',
        url: '/books/scroll',
        data:{
          offset: offset
        },
        dataType: 'json'
      })
      .done(function(data){
        // 返ってきたdataに対して処理を実行
      });
    }
  });
});

ポイントは2点です。

  1. if (target.scrollTop() == 0)
    最上部までscrollしたタイミングでajax通信を行なっています。
  2. const offset = target.children().length;
    tbody内の子要素の数をoffset値として取得し、後ほどコントローラに送っています。こうすることでindex表示後に新規レコードが追加された場合でもoffset値を動的に変更することができます。
3.#scrollにて追加のレコードを取得
app/controllers/books_controller.rb
def scroll
  offset = params[:offset] # ajaxで送られてきたパラメータの取得
  @books = Book.all.order(id: 'DESC').limit(15).offset(offset)
end

インスタンス化した@booksはscroll.json.jbuilderに送られます。

4.jbuilderにてjson形式に変換しデータを返却する
app/views/books/scroll.json.jbuilder
json.array! @books do |book|
  json.title book.title
  json.body book.body
end

jbuilderでの変換例はこちらにいくつか載っていますが、検索すると記事もたくさん出てきますので、そちらの方がわかりやすいと思います。

5.返ってきたデータを元にjs側で処理
app/assets/javascripts/scroll.js
// .done(function(data)の中身
// 返ってきたdataに対して処理を実行
if(data.length != 0){
  data.forEach(function(data){
    const html = `
      <tr>
        <td class='scroll__td'>${data.title}</td>
        <td class='scroll__td'>${data.body}</td>
      </tr>
    `;
    target.prepend(html);
  });
  // scroll位置を調整
  $(target).scrollTop(data.length * 40);
}
最終的なscroll.jsのcodeはこちら
app/assets/javascripts/scroll.js
$(document).on('turbolinks:load', function() {
  // load時最下部までscroll
  const target = $('.scroll__tbody');
  target.scrollTop(600);

  target.scroll(function(){
    if (target.scrollTop() == 0){
      const offset = target.children().length;
      $.ajax({
        type: 'GET',
        url: '/books/scroll',
        data:{
          offset: offset
        },
        dataType: 'json'
      })
      .done(function(data){
        // 返ってきたdataに対して処理を実行
        if(data.length != 0){
          data.forEach(function(data){
            const html = `
              <tr>
                <td class='scroll__td'>${data.title}</td>
                <td class='scroll__td'>${data.body}</td>
              </tr>
            `;
            target.prepend(html);
          });
          // scroll位置を調整
          $(target).scrollTop(data.length * 40);
        }
      });
    }
  });
});

以上で終了です。

####おわりに
今回作成した簡単な例の場合、kaminariやjsライブラリを利用した方が早く実装できたのかもしれません。しかし、実装内容が複雑になってくると、gemやライブラリの利用方法を学んで実装するよりも、自分で記述した方が早い場合もあるのかなと思いました。

6
4
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
6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?