Улучшаем «бесконечный скроллинг. Делаем “ленивую” прокрутку на jQuery

Nokia 05.05.2019
Nokia

Песочница

рекрут 9 февраля 2013 в 21:42 Крути меня полностью. Адаптивный макет с бесконечной прокруткой
  • Чулан *

Не так давно столкнулся с задачей:
- Верстка адаптивного макета. При ресайзе окна, элементы на странице масштабируются до определенной ширины. Если ширина меньше минимальной, количество элементов в ряду становится меньше, в то же время заполняя весь экран по ширине. Смысл понятен - страница должна смотреться на любом мониторе, в т.ч. на мобильных устройствах.
- Подгрузка содержимого происходит при прокрутке страницы, так что выводится лишь часть контента, остальной подгружается по мере прокрутки вниз (аналог стены ВК).
- Появление элементов на странице происходит по очереди, плавным увеличением прозрачности каждого элемента. После того как первый элемент стал непрозрачным, эффект применяется к следующему элементу… и так для всех.

Готового решения не нашел, все пришлось писать с нуля. Возможно вся эта фича бесполезна, но если вдруг кому пригодится - юзаем, тестируем, наслаждаемся:

HTML . Для тестирования по-быстрому сверстал очень простой макет. Чтобы долго не заморачиваться со стилями, подключил bootstrap. Вот такая разметка:
Хеллоу ворлд! // В принципе, инициализировать класс можно и не здесь, и без параметров. Об этом далее. // контейнер, куда выводятся все элементы. // В нашем случае каждый item - отдельный элемент на странице. Их выведем много. // ... много-много item // Делаем очистку, т.к. у наших item"ов float:left

CSS . Тут все просто до безобразия. Подключил bootstrap, задал несколько стилей для контента - и вперед!
@import url("bootstrap.css"); body { background: #000; } section.container { position: absolute; top: 50px; bottom: 50px; width: 100%; overflow: auto;} section.container > section.item { position: relative; float: left; margin: 0; padding: 0; height: 140px; overflow: hidden;} section.container > section.item > img {width: 100%;}

JS . Вот тут уже интересно. Изначально писал функции по-отдельности, потом, когда все заработало обьединил их в один класс
GALLERY = { // задаем дефолтные параметры, которые можно будет переопределить при инициализации. container: "section.container", // родительский контейнер item: "section.item", // отдельный элемент галереи img: "section.item > img.hidden", // изображение, которое мы будем изначально "прятать" interval: 200, // задержка эффекта прозрачности count: 5, // количество добавляемых элементов init: function(params) { var _self = this; // Переопределение параметров (если такие переданы. Если нет - используем по умолчанию.) if(params != undefined){ for (var key in params) { _self = params; } } return _self.setUp(); }, setUp: function() { var _self = this; // Вешаем обработчики событий. В нашем случае такие нужны окну (ресайз) $(window).bind("resize", function(){ _self.adjust(); }); // ... на загрузку страницы $(document).ready(function(){ _self.adjust(); _self.play(); }); // ... на прокутку в родительском блоке $(_self.container).bind("scroll", function(){ _self.checkScroll(); }); }, // подгоняем ширину и количество элементов в ряду, зависимо от размера окна adjust: function() { var _self = this; var outWidth = $(_self.container).width(); var outHeight = $(_self.container).height(); var minWidth = 220; var cnt = Math.floor(outWidth / minWidth); var itemWidth = outWidth / cnt; $(_self.item).css({ "width" : itemWidth + "px" }); }, // Запускаем эффект постепенного появления элементов. Маленький нюанс: Класс hidden // для изображения нужен не за тем чтобы его прятать (это будет делать метод ниже), а для поиска // первого элемента при каждом обновлении контента. Цикл метода play() проходит // по всем изображениям с классом hidden (изначально такой есть у всех), после применения // эффекта fadeIn(), удаляя класс. Таким образом, при подгрузке нового контента, цикл начинается // не с первого элемента, а с первого подгруженного. // Впервые столкнулся с использованием метода queue(). play: function() { var _self = this; var items = $(_self.container).find(_self.img); $(items).each(function(i) { var cur = $(this).hide(); $(document).queue("myQueue", function(n) { cur.removeClass("hidden").fadeIn(_self.interval, n); }); }); $(document).dequeue("myQueue"); }, // Собственно подгрузка контента. Здесь все просто: отправляем запрос на сервер - получаем // новое содержимое на страницу (если такое есть) и выводим его в самом конце родительского блока load: function() { var _self = this; $.ajax({ url: "/load.php", type: "POST", data: {"count": _self.count}, dataType: "json", success: function(json) { if(json.output) { $(_self.container).children("div.clearfix").before(json.output); _self.adjust(); _self.play(); } } }); }, // Проверяем состояние прокрутки. Если вертикальная прокрутка в родительском блоке // равна максимально возможной - отправляем запрос за новым контентом. // Таким образом, подгрузка содержимого будет выполняться только если мы // докрутили страницу до конца. checkScroll: function() { var _self = this; var scrollH = $(_self.container).prop("scrollHeight"); var scrollT = $(_self.container).prop("scrollTop"); var scrollS = $(_self.container).prop("scrollTop") + $(_self.container).height(); if(scrollS == scrollH) { _self.load(); } } }

PHP . Тут все примитивно, только для тестирования.
$json = array(); $html = ""; $items_on_page = !empty($_POST["count"])? (int)$_POST["count"]: 5; for($i = 0; $i < $items_on_page; $i++) { $html .= ""; } $json["output"] = $html; echo json_encode($json);
Здесь в $_POST передается только один параметр (количество добавляемых блоков), но можно передать дополнительно точку старта, по принципу пагинации. Ее можно получить в JS при помощи:
var items = $(_self.container).find(_self.img); var start = items.length; // и передать в запросе: data: {"count": _self.count, "start": start}, // ... после чего при выборке данных указать..." LIMIT " . (int)$_POST["start"] . ", " . $items_on_page;

Вот такая получилась «пагинация без пагинации», да на резиновой страничке. Рабочий пример здесь.

Сколько я себя помню, веб-разработчики всегда обращались к старой-доброй пагинации в случае, когда им необходимо было отобразить большое количество контента. Не поймите меня неправильно, пагинация до сих пор является эффективным способом отображения контента, но в этой статье мы поговорим о другом подходе - “ленивой” прокрутке, также известной под названием “бесконечной прокрутки” и “отказом от пагинации”. С помощью этой техники подгрузка контента производится с помощью AJAX, когда пользователь прокручивает страницу до места, где загруженный контент заканчивается. Ленивая прокрутка используется некоторыми гигантами интернета, такими как Facebook и Pinterest. В этом посте мы попробуем реализовать свой плагин для ленивой загрузки на jQuery.

Преимущества и недостатки

Преимущества такого метода очевидны. Для того, чтобы получить дополнительную порцию контента вам нет необходимости переходить на другую страницу, что также сбивает ваше внимание и заставляет смотреть в другую область страницы. Добавив возможность ленивой загрузки вы удерживаете внимание пользователя на одной и той же области в процессе чтения.

Ленивая загрузка эффективна не во всех ситуациях. Например, в случаях, когда пользователь переходит по ссылке, а затем хочет вернуться на предыдущую страницу с помощью истории браузера, он теряет позицию в той части, которая была подгружена с помощью AJAX. Одним из способов решения подобной проблемы - открывать все ссылки в новом окне или вкладке.

В добавок, перед тем, как решите использовать эту технологию знайте, что поисковые системы не очень-то дружны с такими решениями. Для того, чтобы избежать проблемы видимости контента поисковикам, убедитесь, что вы можете предоставить альтернативу с пагинацией или картой сайта.

Начинаем

Начнем с наброска очень простой страницы. Самые важные части страницы показаны в коде ниже. Полные файлы можно посмотреть в исходниках.

HTML

Привет! Я - первый элемент Привет! Я - второй элемент Привет! Я - третий элемент Итак, это становится скучным... Попробуем кое-что новенькое Как насче того, чтобы загрузить еще элементов посредством AJAX Нажмите на кнопку ниже, чтобы загрузить больше элементов загрузить еще загружаю дополнительные элементы

CSS

#data-container { margin: 10px; } #data-container .data-item { background-color: #444444; border-radius: 5px; -moz-border-radius: 5px; -webkit-border-radius: 5px; padding: 5px; margin: 5px; color: #fff; } #loading-div { display: none; } #button-more{ cursor: pointer; margin: 0 auto; background-color: #aeaeae; color: #fff; width: 200px; height: 50px; line-height: 50px; }

Основные замечания

Если вы приглядитесь к документу, что мы создали, то увидите, что новые посты будут загружены при клике на кнопку “загрузить еще”. Вот пункты, которые мы будем реализовывать:

  • необходимо сделать запрос на URL, который вернет нам список элементов, которые необходимо будет вставить на странице
  • этот процесс должен повторяться каждый раз, как будет нажата кнопка, но в результате должны возвращаться новые посты
  • новые посты должны выдаваться на каждый запрос до тех пор, пока есть, что возвращать
  • когда больше не осталось постов, необходимо сообщить пользователю, что он достиг конца
Шаблоны ответа AJAX

В идеале необходимо объявить переменную, в которой мы будем хранить номер страницы, и с помощью этого номера определять URL, на который мы будем отправлять запрос. В нашем демонстрационном примере у нас будут три таких страницы: 2.html, 3.html и пустая страница 4.html.

Когда вы нажимаете на кнопку загрузки следующей порции данных, проходит некоторое время от момента отправки запроса и получения ответа. В таком случае мы будем прятать кнопку загрузки, а вместо нее будем показывать некоторый текст, говорящий о том, что идет загрузка элементов:

Добавляем полученные данные на страницу

Сначала вернем обратно те изменения, которые мы проделали, пока запрос еще выполнялся, то есть, показать кнопку “загрузить еще”, и спрятать информационный текст. Во-вторых, необходимо вставить полученные данные на страницу, после тех элементов, что уже есть на странице. Заметьте, что для упрощения в этом примере мы получаем данные HTML сразу, как результат запроса. Можно отсылать ответ в формате JSON, добавив в него дополнительные переменные, как статус или сообщение. Код ставки данных представлен ниже:

$(buttonId).show(); $(loadingId).hide(); $(response).appendTo($(container)); page += 1;

Обрабатываем вариант, когда данные закончились

Как только вы достигнете последнего поста, вам нужно показать пользователю, что больше постов нет. Сделать это можно различными способами. Можно отсылать статус в виде встроенного в ответ кода или сообщения. Так как мы напрямую используем разметку HTML в этом примере, то пустой ответ в нашем случае будет говорить о том, что данные закончились.

$.ajax({ ... success: function(response) { // Действие при пустом ответе if (response.trim() == "") { $(buttonId).fadeOut(); $(loadingId).text("No more entries to load!"); return; } // Если ответ верный }, ... });

Заключение

В данном демонстрационном примере мы привели очень базовый вариант обработки ленивой загрузки. Конечно же, можно сделать гораздо лучше, если постараться. Для начала, можно вообще избавиться от кнопки, и отправлять запрос, когда пользователь прокрутит страницу до конца. Это избавит пользователя от дополнительной необходимости нажимать кнопку, и в целом ускоряет процесс просмотра информации. Во-вторых, можно просто отсылать чистые данные в формате JSON, а разметку создавать налету, используя jQuery, например:

$.each(response.items, function(index, value) { $("", { "class" : "data-item", "text" : value }); });

И, наконец, ответ в JSON может содержать сообщение, говорящее, прошел ли запрос корректно; данные; а также информацию о том, есть ли еще посты для загрузки. В данном случае это более эффективный способ отправлять результат.

Сколько я себя помню, веб-разработчики всегда обращались к старой-доброй пагинации в случае, когда им необходимо было отобразить большое количество контента. Не поймите меня неправильно, пагинация до сих пор является эффективным способом отображения контента, но в этой статье мы поговорим о другом подходе - “ленивой” прокрутке, также известной под названием “бесконечной прокрутки” и “отказом от пагинации”. С помощью этой техники подгрузка контента производится с помощью AJAX, когда пользователь прокручивает страницу до места, где загруженный контент заканчивается. Ленивая прокрутка используется некоторыми гигантами интернета, такими как Facebook и Pinterest. В этом посте мы попробуем реализовать свой плагин для ленивой загрузки на jQuery.

Преимущества и недостатки

Преимущества такого метода очевидны. Для того, чтобы получить дополнительную порцию контента вам нет необходимости переходить на другую страницу, что также сбивает ваше внимание и заставляет смотреть в другую область страницы. Добавив возможность ленивой загрузки вы удерживаете внимание пользователя на одной и той же области в процессе чтения.

Ленивая загрузка эффективна не во всех ситуациях. Например, в случаях, когда пользователь переходит по ссылке, а затем хочет вернуться на предыдущую страницу с помощью истории браузера, он теряет позицию в той части, которая была подгружена с помощью AJAX. Одним из способов решения подобной проблемы - открывать все ссылки в новом окне или вкладке.

В добавок, перед тем, как решите использовать эту технологию знайте, что поисковые системы не очень-то дружны с такими решениями. Для того, чтобы избежать проблемы видимости контента поисковикам, убедитесь, что вы можете предоставить альтернативу с пагинацией или картой сайта.

Начинаем

Начнем с наброска очень простой страницы. Самые важные части страницы показаны в коде ниже. Полные файлы можно посмотреть в исходниках.

HTML

Привет! Я - первый элемент Привет! Я - второй элемент Привет! Я - третий элемент Итак, это становится скучным... Попробуем кое-что новенькое Как насче того, чтобы загрузить еще элементов посредством AJAX Нажмите на кнопку ниже, чтобы загрузить больше элементов загрузить еще загружаю дополнительные элементы

CSS

#data-container { margin: 10px; } #data-container .data-item { background-color: #444444; border-radius: 5px; -moz-border-radius: 5px; -webkit-border-radius: 5px; padding: 5px; margin: 5px; color: #fff; } #loading-div { display: none; } #button-more{ cursor: pointer; margin: 0 auto; background-color: #aeaeae; color: #fff; width: 200px; height: 50px; line-height: 50px; }

Основные замечания

Если вы приглядитесь к документу, что мы создали, то увидите, что новые посты будут загружены при клике на кнопку “загрузить еще”. Вот пункты, которые мы будем реализовывать:

  • необходимо сделать запрос на URL, который вернет нам список элементов, которые необходимо будет вставить на странице
  • этот процесс должен повторяться каждый раз, как будет нажата кнопка, но в результате должны возвращаться новые посты
  • новые посты должны выдаваться на каждый запрос до тех пор, пока есть, что возвращать
  • когда больше не осталось постов, необходимо сообщить пользователю, что он достиг конца
Шаблоны ответа AJAX

В идеале необходимо объявить переменную, в которой мы будем хранить номер страницы, и с помощью этого номера определять URL, на который мы будем отправлять запрос. В нашем демонстрационном примере у нас будут три таких страницы: 2.html, 3.html и пустая страница 4.html.

Когда вы нажимаете на кнопку загрузки следующей порции данных, проходит некоторое время от момента отправки запроса и получения ответа. В таком случае мы будем прятать кнопку загрузки, а вместо нее будем показывать некоторый текст, говорящий о том, что идет загрузка элементов:

Добавляем полученные данные на страницу

Сначала вернем обратно те изменения, которые мы проделали, пока запрос еще выполнялся, то есть, показать кнопку “загрузить еще”, и спрятать информационный текст. Во-вторых, необходимо вставить полученные данные на страницу, после тех элементов, что уже есть на странице. Заметьте, что для упрощения в этом примере мы получаем данные HTML сразу, как результат запроса. Можно отсылать ответ в формате JSON, добавив в него дополнительные переменные, как статус или сообщение. Код ставки данных представлен ниже:

$(buttonId).show(); $(loadingId).hide(); $(response).appendTo($(container)); page += 1;

Обрабатываем вариант, когда данные закончились

Как только вы достигнете последнего поста, вам нужно показать пользователю, что больше постов нет. Сделать это можно различными способами. Можно отсылать статус в виде встроенного в ответ кода или сообщения. Так как мы напрямую используем разметку HTML в этом примере, то пустой ответ в нашем случае будет говорить о том, что данные закончились.

$.ajax({ ... success: function(response) { // Действие при пустом ответе if (response.trim() == "") { $(buttonId).fadeOut(); $(loadingId).text("No more entries to load!"); return; } // Если ответ верный }, ... });

Заключение

В данном демонстрационном примере мы привели очень базовый вариант обработки ленивой загрузки. Конечно же, можно сделать гораздо лучше, если постараться. Для начала, можно вообще избавиться от кнопки, и отправлять запрос, когда пользователь прокрутит страницу до конца. Это избавит пользователя от дополнительной необходимости нажимать кнопку, и в целом ускоряет процесс просмотра информации. Во-вторых, можно просто отсылать чистые данные в формате JSON, а разметку создавать налету, используя jQuery, например:

$.each(response.items, function(index, value) { $("", { "class" : "data-item", "text" : value }); });

И, наконец, ответ в JSON может содержать сообщение, говорящее, прошел ли запрос корректно; данные; а также информацию о том, есть ли еще посты для загрузки. В данном случае это более эффективный способ отправлять результат.

В 2011 году очень модно было (по-моему, эта мода пошла от Фейсбука) делать у себя на сайтах «бесконечный скроллинг»: это когда пользователь крутит-крутит колесом мыши, и всё новые результаты поиска подгружаются Аяксом внизу страницы, превращая обычный скроллинг в бесконечный и вызывая у столкнувшегося с этим в первый раз пользователя «когнитивный диссонанс».

Также недавно я видел на этом сайте статью, в которой автор выражал своё недовольство «бесконечным скроллингом» и призывал использовать старую добрую «паджинацию».

Я согласен с автором. Я сам - наркоман, и порою сижу за монитором как зачарованный, со стеклянными глазами, кручу-кручу-кручу волшебное колёсико, не в состоянии пойти по делам/на работу/в магазин/поесть/попить/в туалет, и кручу я его с одной единственной мыслью: «Ну когдааа же вы все уже закончитесь?!» (что поделаешь - я привык доводить дела до конца).

Однажды утром я встал и твёрдо решил: «Хватит это терпеть!».

Представляю вам свой небольшой скрипт , который я на днях написал для своей «социальной сети» (с блекджеком и шлюхами).

Чтобы посмотреть демо, скачайте архив , и запустите файл «test/test.html».

Какие нововведения предлагает этот скрипт:

Поскольку основной «скроллбар» они превратили непонятно во что, нам нужно вернуть нормальный «скроллбар», что мы и сделали, нарисовав серую полоску в правой части страницы, сразу слева от обычного «скроллбара».

То, докуда дошла эта полоска сверху - это сколько результатов выборки мы уже пролистали. Вообще, эта полоска как бы чёрная. Там, где она серая - это место во всей выборке, где мы находимся сейчас.

Обратите также внимание на то, что адрес страницы меняется во время прокрутки. Таким образом, вы можете сегодня вечером пролистать половину, добавить страницу в закладки, выключить компьютер, пойти спать, проснуться утром, включить компьютер, зайти на страницу, добавленную вами вчера перед сном в закладки, и продолжить листать с того самого места, на котором вы остановились. И, более того, вы сможете листать с этого места не только вперёд, но и назад.

И по поводу навигации на любую нужную страницу: попасть на любую страницу можно и здесь - просто подставьте в адрес веб страницы номер нужной вам страницы выборки. Поскольку человекопонятные url"ы сейчас являются стандартом, это должно быть годным решением.

Этот скрипт, очевидно, не является чем-то высеченным в граните, и я сам, встав сегодня с утра, переписал его половину. Тем не менее, работоспособность вроде как стабильная, и проверено в последних версиях Chrome и FireFox. Сразу скажу: не гонюсь за «кроссбраузерностью» - её всегда можно будет допилить, если кто-то захочет использовать этот скрипт на своём «кроссбраузерном» сайте.

Вроде как всё, демо говорит само за себя. Пост провоцирует комментарии.

Что ещё можно сделать (на основе комментариев):

При клике в любое место полоски - переходить на соответствующую страницу выборки.

Убирать страницы данных с верха веб страницы, когда данных на веб странице становится слишком много (чтобы не тормозило).

Примеры улучшенного «бесконечного скроллинга».

Всем привет. Сегодня хочу поговорить с вами о том, как организовать «ленивую загрузку» контента на посадочных страницах.

Часто, ее еще называют «бесконечной прокруткой». Наверняка вы видели подобный эффект, когда контент изначально не присутствует на сайте, а по мере прокрутки страницы он плавно подгружается.

Давненько уже написали в раздел «Ваши предложения», с просьбой написать статью о том, как реализовать такой эффект:


Вот и решил, заняться реализацией. Спасибо, что подкидываете идеи. Приступим…



Как и в большинстве случаев, начинаем с того, что подключаем библиотеку jQuery:

А сейчас нужно немножко отвлечься и я объясню вам суть метода. Все это затевается для того, чтобы не грузить все элементы сайта (ваше портфолио или отзывы), а подгружать их по мере необходимости. Например, при клике пользователя по кнопке «Показать еще». Таким образом, страница будет грузиться намного быстрее. А теперь суть, при помощи ajax технологии мы будем подгружать нужный элемент (div) и стороннего файла на наш лендинг. Вот так все просто, как в теории, так и на практике, и вы в этом скоро убедитесь.

Теперь давайте создадим основной блок, например, с картинками наших работ. Предположим мы рисуем иконки, и будем подгружать их при клике по кнопке. Я создал такую структуру:

Портфолио Показать еще...

Как видите, все — просто. Но на что нужно обратить внимание? А обратить внимание нужно на div с id=»smartPortfolio», id=»moreButton» и id=»loadingDiv», так как они используются в скрипте, который и помогает нам загружать контент с других страниц. SmartPortfolio — это «контейнер» для нашего портфолио. MoreButton — это будет наша кнопка, при клике на которую загружается еще одна часть портфолио. LoadingDiv — область, в которую будет выводиться текст, когда портфолио будет полностью открыто, либо возникнет какая-нибудь ошибка.

Например, часть из читателей все равно будет пытаться тестировать этот скрипт не на сервере, а просто откроют индексный файл в браузере. В таком случае они увидят сообщение об ошибке. Кроме того, при совсем медленном соединении, на загрузку файлов может понадобиться время, и чтобы пользователь понимал, что процесс идет, можно вписать туда сообщение, что идет загрузка данных или поместить (картинку, индикатор прогресса или еще что-то).

Сам скрипт я не писал, а нашел на одном из сайтов, автор указан в исходнике, кому интересно — посмотрите. Так как он не слишком большого размера, то приведу его весь, но если вы планируете использовать такие же названия айдишников и пути к файлам, как у меня, то можете даже не заглядывать в него, а просто подключить перед закрывающимся тегом body (в футере).

Для тех, кто планирует вносить правки вот сам скрипт:

Var lazyload = lazyload || {}; (function($, lazyload) { "use strict"; var page = 2, buttonId = "#moreButton", loadingId = "#loadingDiv", container = "#smartPortfolio"; lazyload.load = function() { var url = "./pages/" + page + ".html"; $(buttonId).hide(); $(loadingId).show(); $.ajax({ url: url, success: function(response) { if (!response || response.trim() == "NONE") { $(buttonId).fadeOut(); $(loadingId).text("Портфолио полностью загружено"); return; } appendContests(response); }, error: function(response) { $(loadingId).text("К сожалению, возникла какая-то ошибка при запросе. Пожалуйста, обновите страницу."); } }); }; var appendContests = function(response) { var id = $(buttonId); $(buttonId).show(); $(loadingId).hide(); $(response).appendTo($(container)); page += 1; }; })(jQuery, lazyload);

Итак, теперь стоит поговорить о тех файлах, из которых мы будем подгружать информацию. Скрипт предполагает, что это будут файла с именами 2.html…5.html и т.д. В которых лежит наша информация. Например, у меня первым подгружается файл 2.html и он имеет такое содержимое:

В моей структуре сайта, страницы из которых будет браться информация для последующей загрузке при клике, лежит в папке pages. Там три файла, два из которых заполнены, а последний — пустой. Это предполагает логика скрипта.

Путь в скрипте указан следующим образом:

Var url = "./pages/" + page + ".html";

Если вы планируете использовать другой путь, не забудьте заменить его в скрипте. Кроме того, если вы использовали другие айдишники, то и их придется переопределить в скрипте. Вот тут:

ButtonId = "#moreButton", loadingId = "#loadingDiv", container = "#smartPortfolio";

И, как я уже говорил, перед закрывающимся тегом body подключаем сам скрипт:

Вот так на landing page можно реализовать «ленивую загрузку». Присылайте еще темы, на которые вы хотели-бы почитать статью на блоге. По мере возможности буду стараться публиковать не запланированный материал, а тот, о котором спрашиваете вы в разделе «Ваши предложения». А на сегодня — все. Пока!



Рекомендуем почитать

Наверх