[Rails]實作捲軸產生無限捲軸功能
最近剛好在實作捲軸下拉時,動態顯示出剩下來分頁的內容,所以參考了RailsCast Endless Page的影片,以下是實作後的筆記。
先做出簡單的頁面
假設我們需要在index page實作列出所有portfolio的效果
我們最一開始的view和controller應該會長這樣
# home/index.html.erb
<div id="products" class="row">
<% @portfolios.each do |portfolio| %>
<div class="col-lg-2 col-sm-6 portfolio-item product">
<div class="card h-100">
<a href="#">
<%= image_tag portfolio.image.url, class: "card-img-top"%>
</a>
</div>
</div>
<% end %>
</div>
# home_controller.rb
def index
@portfolios = Portfolio.all
end
為了等下的實作方便,讓我們來改寫一下目前的頁面,我們要把程式碼移到partial中,然後用render來渲染。
# home/index.html
<div id="products" class="row">
<%= render @portfolios %>
</div>
# portfolios/_potfolio.html.erb
<div class="col-lg-2 col-sm-6 portfolio-item product">
<div class="card h-100">
<a href="#">
<%= image_tag portfolio.image.url, class: "card-img-top"%>
</a>
</div>
</div>
註:render collection的用法,可以參考龍哥的這一篇Layout, Render 與 View Helper的內容。
實作分頁功能
接下來我們要實作分頁功能,因為我們的無限捲軸功能也是透過javascript去產生原本頁面上的分頁來完成的。
首先我們需要使用kaminari這個gem,在Gemfile裡寫入
# Gemfile
gem 'kaminari'
執行bundle安裝gem
bundle install
接下來我們在home_controller中,加上以下的程式碼
def index
@portfolios = Portfolio.all.page(params[:page]).per(6)
end
讓我們看一下我們都新增了哪些東西
- page(params[:page])
這段程式碼可以params判別目前的頁數是第幾頁。比如說當我們點擊第四頁的時候,我們就可以透過params[:page]得到目前的頁數。
http://localhost:3000/?page=4
- per(6)
這代表你每幾個objects一頁。
接下來在view中加入<%= paginate @portfolios %>,這段程式碼可以幫我們產生分頁所需的link。
<div id="products" class="row">
<%= render @portfolios %>
</div>
<%= paginate @portfolios %>
到這邊我們就已經產生了分頁的效果。
處理滾動捲軸的內容
接下來我們要實作的內容是,當向下滾動到某個區域的時候,網頁會自動顯示接下來的分頁。
- 卷軸滾動到特定位置時觸發
- 即時渲染下一個分頁的內容
卷軸滾動到特定位時觸發
首先先在home.coffee中加入以下的程式碼,我們會一步一步來解釋
jQuery ->
$(window).scroll ->
if $(window).scrollTop() > $(document).height() - $(window).height() - 60
alert "Near Bottom"
- $(window).scroll()
代表當我們滾動視窗時,就會觸發內部的程式碼
- $(window).scrollTop()
scrollTop()可以讓我們設定該元素(這裡是window)跟頂部的位移。當你尚未開始滾動時,位移是零。
- $(window).height() 和 $(document).height()
這可以讓我們得到document跟目前視窗的高度
所以這段程式碼的邏輯就是,當頁面向下滾動時,如果頁面的偏移量,大於整個document的高度 - 視窗的高度 - 60時,觸發alert。
即時渲染下一個分頁的內容
接下來讓我們改寫並加入新的程式碼到home.coffee和home/index.js.erb兩個檔案中。
home.coffee程式碼
# home.coffee
jQuery ->
$(window).scroll ->
url = $('.pagination .next a').attr('href')
if url && $(window).scrollTop() > $(document).height() - $(window).height() - 60
$('.pagination').text("載入更多圖片")
$.getScript(url)
- url = $('.pagination .next a').attr('href')
這段程式碼會得到頁面中,Next的anchor的href值。這段程式碼的重要之處在於,我們接下來可以透過url還有query string,幫我們取得「下一頁」應該要渲染出的object,這樣我們就可以透過滾動到底部時取得下一頁的內容。
我們可以看一下以下的截圖。
而我們的url就會是/?page=2,這邊對應的path就是root_path,並加上一段query string(page=2)。
- $('.pagination').text("載入更多圖片")
設置classj為paginate的element中的text。
- $.getScipt(url)
getScript其實是以下程式碼的簡寫
$.ajax({
url: url,
dataType: "script",
success: success
});
目的是用來送出ajax 的GET request。所以我們可以知道getScript會對我們剛剛得到的/?page=2送出一個ajax的GET request。
index.js.erb
接續剛剛上一段的$.getScipt(url),其實就是對url送出get請求。
由於我們的root_path(root 'home#index')對應到的是home_controller下的index method,所以我們如果可以在app/view/home的資料夾下,新增一個index.js.erb檔來處理request format為js的情況。
註:在*.js.erb這類的檔案中,我們可以混用ruby與js,然後編譯成js code後再傳回去給client端。
# home/index.js.erb
$('#products').append('<%= j render(@portfolios) %>');
<% if @portfolios.next_page %>
$('.pagination').replaceWith('<%= j paginate(@portfolios) %>');
<% else %>
$('.pagination').remove();
<% end %>
<% sleep 1 %>
- $('#products').append('<%= j render(
@portfolios) %>');
這段程式碼會在id為products的element後方,插入render(@portfolios)渲染出的html。
這邊另外比較值得注意的是j()的用法。
j()其實是escape_javascript()的縮寫,目的是跳脫雙引號帶來的束縛,不會因為在內部出現多個雙引號導致javascript壞掉。更多內容可以參考這篇Rails為何要使用escape_javascript?。
如果你還記得的話,我們的home_controller中的index方法是
def index
@portfolios = Portfolio.all.page(params[:page]).per(6)
end
當我們對home/index送出get request時,我們還額外送出了一個query string?page=2。所以我們等於是透過送出ajax request還有query string,得到了「下一頁(第二頁)」中的object。
- <% if
@portfolios.next_page %>
$('.pagination').replaceWith('<%= j paginate(@portfolios) %>');
<% else %>
$('.pagination').remove();
<% end %>
這段程式碼代表的意思是,當目前的頁面的下一頁還有object時,將目前的分頁link用下一頁的分頁link取代掉。也就是假設目前是第一頁,當我們往下滾到定點時,用第二頁的分頁link取代第一頁。
這樣的話我們就可以一直不斷地抓取下一頁的內容。
- <% sleep 1 %>
讓程式暫停1秒再繼續執行。這邊可以自行決定要不要使用,如果不希望分頁滾動時渲染太快,可以考慮使用。
參考資料
RailsCast #114 Endless Page
Rails為何要使用escape_javascript?
Why escape_javascript before rendering a partial?
Layout, Render 與 View Helper
註:圖片取自Pixabay 創用CC並自行修改
Leave [Rails]實作捲軸產生無限捲軸功能 to:
Read more #cn posts
Best Posts From linjiahung
We have not curated any of linjiahung's posts yet. But you can encourage our curation team to review posts by visiting them regularly and by referring other readers. Because we give priority to frequently read content.