Бесконечный скролл, т.е. постепенный показ и подгрузка данных как реакция на действия пользователя, закрепился в SPA-приложениях. Я работаю с angular, но в целом это может и к backbone относиться..
Технически, данные на frontend хранятся в какой-то коллекции/сервисе. То что показывается пользователю — подмножество этих данных, т.е. может быть отфильтрованным, отсортированным и изменяемым на лету. Таких подмножеств в приложении может быть несколько, в зависимости от отображения (view).
В одном месте у вас товары одного бренда, в другом месте у вас заказанные товары, в третьем — товары конкретного пользователя.
Что значит «ещё»?
Это значит, что вопрос подгрузки новых данных — нетривиальная задача.
Сначала я подумал что лучше всего запрашивать данные по времени. У каждого ряда данных будет timestamp, как у твиттера, соответсвенно запрос будет высылаться «подгрузи 20 рядов данных, старше X», а время будет браться у последнего ряда.
Но тут возникает несколько проблем.
- Что если у данных одно и то же время? Если на backend добавить вторую сортировку по ID, и передавать ID последнего ряда вместе со временем, то это всё-равно не спасает положения, т.к. значительно усложняется вычисление OFFSET в SQL запросе.
- Если запрос зависит от последнего показанного ряда, то получается что запрос формируется на основе подмножества, т.е. зависит от view. Это плохо, т.к. завязывает модель, контроллер и view в единое целое. Альтернатива — дублировать фильтрацию которая происходит для view в модели.
- Что если данные обновились со времени последнего запроса? Надо ли их добавлять в начало списка?
- Что делать если данные отсортированы не по времени, а по алфавиту? И они вдруг поменялись?
Вобщем приходишь к тому, что для универсального решения, надо по-прежнему работать с OFFSET и ORDER BY, как то было с постраничной навигацией. При этом offset высылается в зависимости от нужды view и он равен размеру подмножества.На случай обновившихся данных, я использую socket.io, который оповещает что надо обновить. Добавившиеся данные я сразу внедряю в список, а update происходит автоматически
Когда загружать?
ngInfiniteScroll — простенькая ангулярная директива для ангуляра, которая триггерит нашу функцию подгрузки если вы достаточно проскроллили вниз (scrollTop() близок к высоте элемента). Проблемы в ней две — она привязана ко всему окну (window). Обычно же у нас может быть несколько контейнеров на странице где надо подгружать данные. Есть и более простой пример
И вторая проблема следующая из неё — если вы запросили слишком мало данных за запрос (LIMIT 5), и/или их высота динамическая и меньше чем высота контейнера, то ваш scroll никогда не появится и не запустит постоянное дополнение списка. Пришлось это поисправлять, каждый раз расчитывая высоту содержания контейнера.
Тут главное не заDDOSить сервер из-за неправильного расчёта высоты элементов и рекурсии. По-хорошему вызов подгрузки надо начинать до достижения нижнего края.
Search
Есть небольшая проблема, если у вас есть фильтрация коллекции с поиском. Я решаю эту проблему отдельным мини-кэшем и отдельными запросами. Если вы ищете что-то по ключевому слову, то результаты не добавляются в основную коллекцию списка, поскольку это повлияет на offset при нормальном режиме. Search и отфильтрованные данные у меня подгружаются без постраничной навигации, т.е. их количество ограничено
Когда использовать не стоит
Бесконечный скролл напрямую зависит от скролла. Замечали сайты на которых открываешь в телефоне страничку с контактами, а там google map на всю высоту экрана? И не добраться до текста с телефонами ниже.. Бесконечный скролл, если он достаточно высокий, аналогично блокирует данные ниже себя.
Во-вторых, бесконечность дезориентирует и лишает контроля. Вы не знаете сколько вы уже проскроллили, дажа примерно глядя на размер бегунка, не можете понять сколько осталось до конца. Аналогично, вы не можете перепрыгнуть в «значительно более далёкие» версии данных.
В-третьих, вы не можете скинуть URL-ссылку на это место другому человеку и соответственно, если вы уйдёте с бесконечной ленты по ссылке и потом попробуете вернуться назад, вы попадёте на начало ленты. Т.е. такой скролл требует изменения поведения при навигации и пользователь должен открывать ссылки в новом окне.
Само собой, если у вас SPA, то могут быть проблемы с индексацией поисковиками. Гугл предлагает гибридный вариант, где данные таки будут доступны и по страницам тоже.
Наконец, если у вас что-то случилось с ajax-запросом и нет хорошей обработки ошибок, бесконечный скролл ломает глубину просмотра. А если сервер достаточно нестабильный, то риск что пользователь никогда не сможет добраться до нужных данных увеличивается с каждым запросом. Например если вероятность ответа сервера HTTP 500 оценивается в 5%, то после 15ти последовательных запросов вероятность что лента будет сломана уже (100 — 100 × 0.9515) = 55%.
Тем не менее, если вы делаете крутой динамический сайт, который может обновляется блоками с нестатичными размерами и с нестандартной навигацией вверх/вниз и вправо/влево, постепенная подгрузка musthave.