Подгрузка списка порциями - кнопка «Показать ещё» и прокрутка
Догружаем список без перезагрузки страницы: штатная подгрузка в каталоге, своя порция через контроллер, адреса страниц для поиска и защита от повторов в выдаче.
Что нужно знать заранее
В каталоге подгрузка уже есть, и начинают всегда с неё. Компоненты списка умеют показывать кнопку догрузки и подгружать следующую страницу при прокрутке до низа списка.
Своя подгрузка нужна лишь там, где список собирается собственным кодом проекта. Тогда порцию отдаёт действие контроллера, а браузер дописывает полученную разметку в конец контейнера.
Адреса страниц списка обязаны продолжать работать и после включения любой догрузки. Поисковый робот и посетитель без скриптов должны получать те же товары обычной постраничной навигацией.
Шаги
- Проверить, решается ли задача штатными параметрами того компонента, который выводит список.
- Завести действие контроллера, которое отдаёт готовую разметку ровно одной порции списка.
- Собрать выборку порции с постраничкой и устойчивой сортировкой, без повторов записей.
- Дописать разметку в контейнер списка и обновить адрес страницы в истории браузера.
- Спрятать кнопку, когда порции кончились, и оставить рабочими адреса всех страниц.
Решение
Включаем штатную подгрузку в каталоге:
$APPLICATION->IncludeComponent('bitrix:catalog.section', '', [ 'IBLOCK_ID' => $iblockId, 'PAGE_ELEMENT_COUNT' => 12, 'LAZY_LOAD' => 'Y', // кнопка догрузки под списком 'LOAD_ON_SCROLL' => 'Y', // догрузка при прокрутке до конца списка 'MESS_BTN_LAZY_LOAD' => 'Показать ещё',]);// штатная подгрузка сохраняет обычную постраничную навигацию для поисковых роботовОтдаём свою порцию действием контроллера:
public function pageAction(int $page = 1): string{ ob_start(); $GLOBALS['APPLICATION']->IncludeComponent('local:news.portion', '', ['IBLOCK_ID' => 5, 'PAGE' => max(1, $page)], false, ['HIDE_ICONS' => 'Y']); return ob_get_clean(); // действие возвращает готовую разметку порции строкой}Разметку собирает тот же компонент, что и первую страницу списка. Иначе вёрстка догруженных карточек однажды разойдётся с исходной, и заметит это уже заказчик.
Выбираем порцию с устойчивой сортировкой:
$nav = ['nPageSize' => 12, 'iNumPage' => $page, 'checkOutOfRange' => true];$res = CIBlockElement::GetList(['SORT' => 'ASC', 'ID' => 'DESC'], ['IBLOCK_ID' => $iblockId, 'ACTIVE' => 'Y'], false, $nav, ['ID', 'NAME', 'DETAIL_PAGE_URL']);printf("страниц всего: %d\n", $res->NavPageCount);// вторым полем сортировки берут идентификатор: он разводит записи с равным весомСортировка только по одному полю даёт повторы между порциями. Записи с одинаковым значением база возвращает в произвольном порядке, и один товар приезжает дважды, а другой не приезжает вовсе.
Дописываем порцию и правим адрес:
BX.ajax.runAction('local:news.list.page', { data: { page: nextPage } }) .then((response) => { document.querySelector('#list').insertAdjacentHTML('beforeend', response.data); history.replaceState(null, '', '?PAGEN_1=' + nextPage); // адрес отражает показанное if (nextPage >= totalPages) { button.remove(); } // порции кончились });Обновление адреса возвращает посетителя туда же после перезагрузки. Без него человек, открывший товар и нажавший «назад», снова видит первую порцию и уходит с сайта.
Кэшируем порцию с номером страницы в ключе:
$cache = \Bitrix\Main\Data\Cache::createInstance();if ($cache->initCache(3600, 'portion_' . $iblockId . '_' . $page, '/news/portions')) { $items = $cache->getVars();} elseif ($cache->startDataCache()) { /* выборка и $cache->endDataCache(['items' => $items]); */ }// номер страницы обязан входить в ключ, иначе все порции отдадут первую двенадцаткуТипичные проблемы
Один товар приезжает в двух порциях подряд.
Сортировка идёт по одному полю, и записи с равным значением возвращаются в произвольном порядке. Вторым полем сортировки добавляют идентификатор элемента, и порядок выдачи становится устойчивым.
Кнопка догружает одну и ту же порцию.
Номер страницы не входит в ключ кэша либо не увеличивается на стороне браузера. Проверяют оба места сразу: ключ кэша порции и счётчик страниц в скрипте.
Поисковый робот видит только первую страницу.
Постраничная навигация заменена кнопкой догрузки, и обычные адреса страниц перестали работать. Адреса страниц оставляют рабочими, а кнопку догрузки добавляют поверх обычной навигации.
После возврата назад список снова начинается сверху.
Адрес страницы не обновляется при догрузке, и браузер открывает исходный первый список. Номер показанной страницы пишут в адрес браузера при каждой догрузке порции.
Вёрстка догруженных карточек отличается от первых.
Порция собирается отдельным куском разметки в контроллере, а не тем же компонентом. Порцию отдают тем же компонентом и тем же шаблоном, что и первую страницу.
Частые вопросы
Когда хватает штатной подгрузки?
Почти всегда в каталоге: у списков товаров есть кнопка догрузки и догрузка при прокрутке. Своё решение нужно спискам, собранным собственным кодом.
Что вернуть из действия: разметку или данные?
Разметку, если карточки собирает компонент, и данные, если разметку рисует скрипт. Смешанный вариант рано или поздно разводит вёрстку по двум местам.
Как не потерять индексацию?
Оставить рабочие адреса страниц списка и обновлять адрес при догрузке. Робот пройдёт по обычной навигации, а посетитель получит бесконечную ленту.
Почему товары повторяются между порциями?
Сортировка неустойчива: записи с одинаковым весом каждый раз выстраиваются по-разному. Вторым полем сортировки берут идентификатор элемента.
Нужно ли кэшировать порции?
Да, если выборка тяжёлая: порция кэшируется так же, как обычная страница списка. Номер страницы и все условия фильтра обязаны входить в ключ кэша.
Смежное
- AJAX и контроллеры - оглавление подтемы
- AJAX-запрос: контроллер, свой файл и ответ в JSON - как устроено действие контроллера
- Не работает AJAX-запрос: разбор причин - разбор молчащего запроса
- Постраничная навигация: страницы в выборке, свой вызов, кэш - обычная навигация по страницам
- Кэширование своей выборки: ключ, теги, сброс - ключ кэша порции
- Витрина каталога: список раздела, сортировка, фильтр - штатный список товаров раздела
- Дубли страниц каталога: канонический адрес, пагинация, фильтр - адреса страниц и поисковые роботы
- JS и интерфейсы - устройство клиентской части платформы целиком