ボクココ

個人開発に関するテックブログ

Rails でシンプルな無限スクロール実装

ども、@kimihom です。

久々に無限スクロールを実装する機会が 詠みラボで発生しました。そこで、振り返りがてら、こんな感じで実装するのが個人的に一番シンプルだったコードをご紹介します。

サーバー側実装

俳句(Haiku) をそれぞれ20句ごとに毎回取得し、スクロール下部についたら新しく20件を取得します。HTMLの生成は Rails サーバー側で実施し、それをフロントエンド側では単に HTML を追加するだけの処理になっています。

# Gemfile
gem "kaminari"

# config/routes.rb
resources :haiku

# app/models/haiku.rb
class Haiku < ApplicationRecord
  paginates_per 20
end

# app/controllers/haiku_controller.rb
class HaikuController < ApplicationController
  before_action :authenticate_user!, only: [:new, :create, :update]

  def index
    @haikus = Haiku
      .page(params[:page])
      .order("created_at DESC")

    # 重要. スクロール時はlayoutを含めない
    return render layout: false if params[:no_layout]
  end

end
<%# app/views/haiku/index.html.erb %>
<div id="haikuCmp">
  <% @haikus.each do |haiku| %>
    <div class="ku">
      <%= haiku.kigo #etc %>
    </div>
  <% end %>
</div>
// app/assets/javascripts/haiku.js
$(function() {

  let isScrollLoading = false;
  let scrollPage = 1;
  $(window).scroll(function() {
    if (!isScrollLoading && $(window).scrollTop() + $(window).height() > $(document).height() - 100) {
      isScrollLoading = true;

      scrollPage += 1;
      $.ajax({url: "/haiku", data: {
        "page": scrollPage, "no_layout": "true"
      }}).done(function(data) {
        let appends = $(data).find(".haikusZone").html();
        if (appends.trim().length == 0) return;
        $("#haikuCmp .haikusZone").append(appends);
        isScrollLoading = false;
      }).fail(function(e) {
        console.error("error", e);
      })
    }
  });

});

ポイント

ほとんどが Rails での基本コードに近いです。唯一 "重要" と記した場所を詳しく解説します。

    # 重要. スクロール時はlayoutを含めない
    return render layout: false if params[:no_layout]

まず、ここには if 文となっています。no_layout のパラメータがない限りここを通らずに 通常の app/views/layouts/application.html.erb が呼ばれ、基本のヘッダで CSS, JavaScript などの読み込みなどのコード全てが含まれるようになっています。

しかし、スクロールした後のHaiku一覧取得では、あくまでHaikuの追加したいリストだけを返すようにします。そのため、layout: false を定義しています。これを定義せずに、スクロールして返すHTMLでも<html><head>... が返ってくると、実装はより面倒になります。以下の3行でシンプルに収まるコードになりませんので、実際どうなるか、気になる方は調べてみてください。

let appends = $(data).find("#haikuCmp").html();
if (appends.trim().length == 0) return;
$("#haikuCmp").append(appends);

個人的なお気に入りは $(data).find("#haikuCmp").html();です。no_layout: true でAjaxで送ったサーバーから 、レスポンスがきたHTMLを、一括で追加 appendするだけです。まさに意図した通りに動きます。

サーバーから#haikuCmp の中身が空白以外の何もないデータが返れば、もうそれ以上データがないとしてスクロールを停止します。

終わりに

「いやいや、まだ jQuery かよ・・」そんな声が聞こえてきそうな記事です。

jQuery で慣れたら秒速でコードを組み立てることができるようになりました。この開発スピード感が楽しく、個人開発を進めるモチベーションに繋がっています。

引き続きこっそり改善を続けていきたいと思います。

www.yomilab.com