Нумерация страниц — очень распространенная и широко используемая навигационная техника, и на то есть веские причины. Прежде всего, рассмотрим производительность. Загрузка всех доступных записей в одном запросе может быть очень дорогой. Более того, пользователь может быть заинтересован только в нескольких самых последних записях (т. Е. В последних публикациях в блоге) и не хочет ждать загрузки и отображения всех записей. Кроме того, разбиение на страницы облегчает чтение страницы, не заполняя ее содержимым.
В настоящее время многие веб-сайты используют немного другую технику, называемую бесконечной прокруткой (или бесконечной страницей ). По сути, когда пользователь прокручивает страницу вниз, все больше записей загружаются асинхронно с использованием AJAX. Таким образом, прокрутка кажется более естественной и может быть проще для пользователя, чем постоянное нажатие на ссылку «Следующая страница».
В этой статье я собираюсь объяснить, как реализовать бесконечную прокрутку вместо классической нумерации страниц.
Сначала мы подготовим наш демонстрационный проект, реализуя базовую нумерацию страниц, используя гем will_paginate
. Эта нумерация страниц станет бесконечной прокруткой по мере прохождения учебника. Это потребует написания некоторого кода JavaScript (и CoffeeScript) вместе с нашим Ruby.
Предоставленное решение откатится на нумерацию страниц по умолчанию, если у пользователя отключен JavaScript в браузере. Наконец, наш взгляд практически не требует изменений, поэтому вы можете легко реализовать это на любом веб-сайте.
Другие предметы, которые будут покрыты:
- Как реализовать кнопку «Загрузить еще» вместо бесконечной прокрутки, очень похожую на
используется на SitePoint . - Некоторые ошибки и потенциальные проблемы, в частности, как может помочь History API и шпионская прокрутка.
Рабочую демонстрацию можно найти по адресу http://sitepoint-infinite-scrolling.herokuapp.com .
Исходный код можно найти на GitHub .
Звучит неплохо? Давай прокатимся!
Подготовка проекта
В этой статье я буду использовать Rails 3.2.16, но вы можете реализовать то же решение с Rails 4.
Давайте создадим очень простой блог. На данный момент он будет отображать только демонстрационные сообщения.
$ rails new infinite_scrolling -T
-T
здесь означает, что мы хотим пропустить генерацию набора тестов (я предпочитаю RSpec, но, конечно, вы можете опустить этот флаг).
Мы собираемся подключить некоторые драгоценные камни, которые пригодятся:
Gemfile
gem 'will_paginate', '~> 3.0.5' gem 'betterlorem', '~> 0.1.2' gem 'bootstrap-sass', '~> 3.0.3.0' gem 'bootstrap-will_paginate', '~> 0.0.10'
will_paginate
будет, ну, ну же, разбивать наши записи Я более подробно расскажу об этом драгоценном камне в следующем разделе. betterlorem
создает демо-текст в наших записях. Существуют и другие подобные драгоценные камни, которые создают текст «Lorem Ipsum», но я считаю, что он наиболее удобен для нашего случая (мы собираемся использовать его в seeds.rb
, а не в представлении).
Нет смысла создавать удостоенный наград дизайн, поэтому в качестве быстрого и простого (хотя и не самого маленького, учитывая вес) решения мы будем использовать Twitter Bootstrap 3. Самоцвет bootstrap-sass
добавляет его в наш проект Rails. bootstrap-will_paginate
содержит некоторые стили Bootstrap для самой нумерации страниц.
Не забывай бегать
$ bundle install
Теперь добавьте
//= require bootstrap
в application.js
и
@import "bootstrap";
в application.css.scss
чтобы включить все стили и сценарии Bootstrap. Конечно, в реальном приложении вы бы выбрали только необходимые компоненты.
Модель
Там будет только одна таблица: пост. Это будет очень просто и содержать следующее
колонки:
-
id
(целое число, первичный ключ) -
title
(строка) -
body
(текст) -
created_at
(дата / время) -
updated_at
(datetime)
Бег
$ rails g model Post title:string body:text $ rake db:migrate
создаст соответствующую миграцию и затем применяет ее к базе данных.
Следующим шагом является создание некоторых тестовых данных. Самый простой способ сделать это — использовать seeds.rb
seeds.rb
50.times { |i| Post.create(title: "Post #{i}", body: BetterLorem.p(5, false, false)) }
Это создает 50 сообщений с body
созданным BetterLorem
. Каждый набор генерируемого контента состоит из 5 параграфов. Последние два аргумента говорят BetterLorem
обернуть текст в тег p
и включить конечный период.
Бег
$ rake db:seed
заполнит нашу базу данных несколькими тестовыми сообщениями. Потрясающие!
Последнее, что нужно сделать — это создать PostsController
с методами index
и show
вместе с соответствующими представлениями ( index.html.erb
и show.html.erb
). Также не забудьте настроить маршруты:
routes.rb
resources :posts, only: [:index, :show] root to: 'posts#index'
Наконец, если вы используете Rails 3, обязательно удалите файл public/index.html
.
Контроллер
Мы готовы перейти к веселой части. Сначала давайте отобразим постраничные посты с усеченным телом. Для этого мы будем использовать will_paginate — простой, но удобный драгоценный камень от Mislav Marohnić, который работает с Ruby on Rails, Sinatra, Merb, DataMapper и Sequel.
Существует альтернатива этому решению — kaminari от Akira Matsuda, который является более мощным и более сложным. Вы также можете попробовать. По сути, не имеет значения, какой драгоценный камень вы используете.
В нашем контроллере:
posts_controller.rb
@posts = Post.paginate(page: params[:page], per_page: 15).order('created_at DESC')
Вызов метода paginate
принимает параметр page
сообщающий ему, какой параметр GET использовать для получения требуемого номера страницы. Параметр per_page
указывает, сколько записей должно отображаться на странице. Опция per_page
может быть указана для всей модели или для всего проекта следующим образом:
post.rb
class Post self.per_page = 10 end
will_paginate.rb (в инициализаторе)
WillPaginate.per_page = 10
Метод paginate
возвращает ActiveRecord::Relation
поэтому мы можем связать вызов метода order
.
Вид
index.html.erb
<div class="page-header"> <h1>My posts</h1> </div> <div id="my-posts"> <%= render @posts %> </div> <div id="infinite-scrolling"> <%= will_paginate %> </div>
Заголовок страницы указывается с помощью класса Bootstrap. Следующий блок, #my-posts
, содержит наши постраничные посты. При использовании render @posts
каждый пост из массива отображается с использованием частичного _post.html.erb
. Последний блок, #infinite-scrolling
, содержит элементы управления нумерацией страниц.
Обратите внимание, что will_paginate
достаточно умен, чтобы понять, что мы хотим @posts
на страницы @posts
. Вы можете указать это явно так: will_paginate @posts
.
Вот наш пост частичный.
_post.html.erb
<div> <h2><%= link_to post.title, post_path(post) %></h2> <small><em><%= post.timestamp %></em></small> <p><%= truncate(strip_tags(post.body), length: 600) %></p> </div>
Мы заключаем каждое сообщение в div
, а затем отображаем заголовок, который служит ссылкой для чтения всего сообщения. Отметка времени указывает, когда сообщение было создано. Этот метод timestamp
определяется внутри модели следующим образом:
post.rb
def timestamp created_at.strftime('%d %B %Y %H:%M:%S') end
Наконец, мы используем метод strip_tags
чтобы удалить все теги из записи и strip_tags
чтобы удалить все, кроме первых 600 символов. На этом наша работа заканчивается представлениями (я опустил разметку для layout.html.erb
и show.html.erb
как это не так важно; вы можете проверить это в репозитории GitHub ).
Бесконечная прокрутка
Мы готовы превратить нашу нумерацию страниц в бесконечную прокрутку. jQuery поможет нам с этой задачей.
Создайте новый файл pagination.js.coffee
в каталоге javascripts
.
pagination.js.coffee
jQuery -> if $('#infinite-scrolling').size() > 0 $(window).on 'scroll', -> more_posts_url = $('.pagination .next_page a').attr('href') if more_posts_url && $(window).scrollTop() > $(document).height() - $(window).height() - 60 $('.pagination').html('<img src="/assets/ajax-loader.gif" alt="Loading..." title="Loading..." />') $.getScript more_posts_url return return
Здесь мы привязываем событие scroll
к окну, только если на странице присутствует разбиение на страницы. Когда пользователь прокручивает, извлекает ссылку на следующую страницу — при ее посещении Rails загружает записи с этой страницы (нам все еще нужно внести некоторые изменения в контроллер, чтобы это работало).
Затем убедитесь, что URL-адрес присутствует, и пользователь прокрутил до конца страницы минус 60px
. Это когда мы хотим загрузить больше сообщений. Это значение 60px
является произвольным, и вы, вероятно, захотите изменить его для своего случая.
Если эти условия выполняются, мы заменяем нашу пагинацию «загрузочным» GIF-изображением, которое можно бесплатно загрузить с ajaxload.info . Последнее, что нужно сделать, — это выполнить асинхронный запрос, используя URL, который мы получили ранее. $.getScript
загрузит JS-скрипт с сервера и затем выполнит его.
Обратите внимание на две инструкции по return
. По умолчанию CoffeeScript вернет последнее выражение (та же концепция применима к Ruby), но здесь мы не хотим, чтобы функция jQuery или обработчик событий возвращали что-либо, поэтому указание return
означает «ничего не возвращать».
PostController#index
метод PostController#index
должен отвечать как на HTML, так и на JavaScript. Мы собираемся использовать respond_to
для достижения этого:
posts_controller.rb
@posts = Post.paginate(page: params[:page], per_page: 15).order('created_at DESC') respond_to do |format| format.html format.js end
Последнее, что нужно сделать, это создать представление, которое будет отображаться при ответе с помощью JS:
index.js.erb
$('#my-posts').append('<%= j render @posts %>'); <% if @posts.next_page %> $('.pagination').replaceWith('<%= j will_paginate @posts %>'); <% else %> $(window).off('scroll'); $('.pagination').remove(); <% end %>
Мы обрабатываем больше постов, добавляя их в блок #my-posts
. После этого проверьте, не осталось ли еще страниц. Если это так, замените текущий блок нумерации страниц (который в данный момент содержит изображение «загрузки») новой нумерацией страниц. В противном случае мы удаляем элементы управления нумерацией страниц и отсоединяем событие scroll
от window
, так как больше нет смысла его слушать.
На данный момент наша бесконечная прокрутка готова. Даже если у пользователя отключен JavaScript в браузере, он будет представлен с нумерацией страниц по умолчанию, к которой применен стиль, благодаря bootstrap-will_paginate
.
Стоит упомянуть одну вещь: прокрутка вызовет множество событий scroll
. Если вы хотите отложить обработку этого события, вы можете использовать библиотеку OpenSource BindWithDelay, написанную Брайаном Гринстедом. Чтобы использовать его, просто скачайте его и включите в проект. Затем внесите следующие изменения в скрипт:
pagination.js.coffee
$(window).bindWithDelay 'scroll', -> # the code , 100
Это задержит запуск события на 100 мс. $(window).off('scroll');
внутри index.js.erb
все равно будет index.js.erb
события, поэтому никаких изменений там не требуется.
На этом первая часть статьи заканчивается. В следующей части мы поговорим о кнопке «Загрузить еще» и некоторых проблемах, возникающих при использовании бесконечной прокрутки. Спасибо за чтение и до скорой встречи!