####はじめに
「Rails 無限スクロール」と検索するとたくさんの記事が出てきます。kaminariやjsライブラリを利用して作成している記事をたくさん見かけますが、今回は自分の学習のためにもgemやライブラリなしで実装をしていきたいと思います。(デフォルトのgemであるjbuilderと記法を変更するだけのjqueryは利用します。)
####前提条件(無限スクロール実装前)
簡単な例を作成して記事を書いていきます。下記の条件でbooksテーブルに100件レコードを作成しました。
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
<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>
.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点は機能を満たすために最低限必要です。
- tbodyのstyle
- tdのheight
tdのheightについては、scroll出来ればいいので次の条件を満たせば問題ありません。
tdのheight * 初期表示レコード数 > tbodyのheight
####無限スクロール機能の作成
では、現在全てのレコードが表示されているbooks一覧を15件ずつ取得し表示するように変更していきたいと思います。
#####処理の流れ
- #indexにて最新15件を取得し表示
- scroll範囲最上部までscrollするとイベント発火
- #scrollにて追加のレコードを取得
- jbuilderにてjson形式に変換しデータを返却する
- 返ってきたデータを元にjs側で処理
#####実装
######1. #indexにて最新15件を取得し表示
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部分を変更します。
<% @books.each do |book| %>
<%# 上の記述を下のように変更します %>
<% @books.reverse_each do |book| %>
######2.scroll範囲最上部までscrollするとイベント発火
$(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点です。
- if (target.scrollTop() == 0)
最上部までscrollしたタイミングでajax通信を行なっています。 - const offset = target.children().length;
tbody内の子要素の数をoffset値として取得し、後ほどコントローラに送っています。こうすることでindex表示後に新規レコードが追加された場合でもoffset値を動的に変更することができます。
3.#scrollにて追加のレコードを取得
def scroll
offset = params[:offset] # ajaxで送られてきたパラメータの取得
@books = Book.all.order(id: 'DESC').limit(15).offset(offset)
end
インスタンス化した@booksはscroll.json.jbuilderに送られます。
4.jbuilderにてjson形式に変換しデータを返却する
json.array! @books do |book|
json.title book.title
json.body book.body
end
jbuilderでの変換例はこちらにいくつか載っていますが、検索すると記事もたくさん出てきますので、そちらの方がわかりやすいと思います。
5.返ってきたデータを元に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はこちら
$(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やライブラリの利用方法を学んで実装するよりも、自分で記述した方が早い場合もあるのかなと思いました。