1С-Битрикс. LazyLoad или ленивая загрузка контента при включенном кешировании
В этой заметке я хочу рассказать о технологии LazyLoad или в простонародье «ленивая загрузка»(её ещё называют бесконечный скроллинг или Infinity Scroll) в Битриксе. Метод, описанный мной, будет точно работать в компонентах catalog.section и news.list.
Файлы компонентов, мы конечно же, модифицировать не будем.
«Ленивая загрузка» позволяет снизить нагрузку на сервер, а так же намного быстрее показывать нужный контент пользователю. Зачем ему перезагружать всю страницу целиком, если можно подгрузить только нужный фрагмент страницы. В нашем случаем — это элементы, будь то, элементы новостей или товары каталога.
Суть такого механизма проста: пользователю сначала показывается, например, 15 новостей и потом, как только он докручивает до их конца, сразу же подгружаются следующие 15 элементов. И так, пока не закончатся элементы.
Технические детали
В конце, после списка элементов, мы будем вставлять пустую ссылку, на следующие элементы. В ссылку будем добавлять параметр AJAX_PAGE, он нам ещё понадобится.
Выглядеть она будет примерно так:
/news/?PAGEN_2=2&AJAX_PAGE=Y
Формировать ссылку будем следующим образом:
$paramName = 'PAGEN_'.$arResult['NAV_RESULT']->NavNum; $paramValue = $arResult['NAV_RESULT']->NavPageNomer; $pageCount = $arResult['NAV_RESULT']->NavPageCount; if ($paramValue < $pageCount) { $paramValue = (int) $paramValue + 1; $url = htmlspecialcharsbx( $APPLICATION->GetCurPageParam( sprintf('%s=%s', $paramName, $paramValue), array($paramName, 'AJAX_PAGE',) ) ); echo sprintf('<div class="ajax-pager-wrap"> <a class="ajax-pager-link" data-wrapper-class="news-list" href="%s"></a> </div>', $url); }
Способ первый: без кеширования
Этот способ самый простой и первым приходит в голову.
В шаблон компонента, до вывода элементов вставляем такую строку:
if(isset($_GET['AJAX_PAGE'])) { $APPLICATION->RestartBuffer(); }
А после вывода элементов вставляем это:
if(isset($_GET['AJAX_PAGE'])) { die(); }
Что мы делаем:
Если страницу открываем при помощи AJAX, то перед выводом элементов сбрасываем буфер контента функцией $APPLICATION->RestartBuffer(). А в конце просто выходим. Тем самым мы отдаем только нужный кусок контента при аякс запросе.
Но в этом способе есть большой минус. Функция RestartBuffer() не будет работать при включенном кешировании.
Способ второй: с кешированием
Тут нам на помощь приходит файл component_epilog.php. Он выполняется при каждой загрузке страницы и запускается после того, как отработал шаблон. Как раз то, что нам нужно.
Теперь в шаблон компонента, до вывода элементов и после вывода элементов вставляем html комментарий:
<!--RestartBuffer-->
В папке с темой для нужного компонента создаем файл component_epilog.php и вставляем в него этот код:
if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true) die(); if (isset($_GET['AJAX_PAGE'])) { $content = ob_get_contents(); ob_end_clean(); $APPLICATION->RestartBuffer(); list(, $content_html) = explode('<!--RestartBuffer-->', $content); echo $content_html; die(); }
Пример JavaScript кода
(function(){ var ajaxPagerLoadingClass = 'ajax-pager-loading', ajaxPagerWrapClass = 'ajax-pager-wrap', ajaxPagerLinkClass = 'ajax-pager-link', ajaxWrapAttribute = 'wrapper-class', ajaxPagerLoadingTpl = ['<span class="' + ajaxPagerLoadingClass + '">', 'Загрузка…', '</span>'].join(''), busy = false, attachPagination = function (wrapperClass){ var $wrapper = $('.' + wrapperClass), $window = $(window); if($wrapper.length && $('.' + ajaxPagerWrapClass).length){ $window.on('scroll', function() { if(($window.scrollTop() + $window.height()) > ($wrapper.offset().top + $wrapper.height()) && !busy) { busy = true; $('.' + ajaxPagerLinkClass).click(); } }); } }, ajaxPagination = function (e){ e.preventDefault(); busy = true; var wrapperClass = $('.'+ajaxPagerLinkClass).data(ajaxWrapAttribute), $wrapper = $('.' + wrapperClass), $link = $(this); if($wrapper.length){ $('.' + ajaxPagerWrapClass).append(ajaxPagerLoadingTpl); $.get($link.attr('href'), {'AJAX_PAGE' : 'Y'}, function(data) { $('.' + ajaxPagerWrapClass).remove(); $wrapper.append(data); attachPagination(wrapperClass); busy = false; }); } }; $(function() { if($('.'+ajaxPagerLinkClass).length && $('.'+ajaxPagerLinkClass).data(ajaxWrapAttribute).length){ attachPagination($('.'+ajaxPagerLinkClass).data(ajaxWrapAttribute)); $(document).on('click', '.' + ajaxPagerLinkClass, ajaxPagination); } }); })();
Объясню, что происходит. Теперь при открытии страницы при помощи AJAX запроса, отрабатывает шаблон и вызывается файл component_epilog.php. В нем мы получаем весь контент из буфера, разбиваем его по нашему html комментарию и отдаем клиенту.
Вот такой вот хитрый способ :)
Обновлено 1.12.14
В комментариях к заметке и в письмах на почту люди спрашивают, как быть с включенным композитным режимом? Ведь второй способ «с включенным кешированием» не заработает. И совершенно верно.
Способ третий: с включенным композитным режимом
Если посмотреть на блок-схему работы технологии, то можно заметить, что после исполнения страницы и перед записью её в кеш композита, вызывается событие OnEndBufferContent (оно же вызывается и без технологии). На нём мы и сыграем.
Привожу код обработчика для композитного режима:
// при включенном композитном режиме, сохраняем в кеш, контент нужный для отдачи аяксом \Bitrix\Main\EventManager::getInstance()->addEventHandlerCompatible('main', 'OnEndBufferContent', function(&$content){ if (version_compare(SM_VERSION, '14.5.0') >= 0 && CHTMLPagesCache::IsCompositeEnabled()) { if (isset($_SERVER['HTTP_X_REQUESTED_WITH'], $_GET['AJAX_PAGE']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') { list(, $content_html) = explode('<!--RestartBuffer-->', $content); if(is_string($content_html) && strlen($content_html)){ $content = $content_html; } } } });
Добавить его нужно в init.php вашего сайта.
Если в настройках композита стоит галочка «Сохранять на диск только страницы без параметров», то в поле «а также страницы содержащие только следующие параметры» надо добавить параметр AJAX_PAGE.
И хочу заметить, что при этом способе код приведённый во втором пункте для файла component_epilog.php с включенным кеширование нужно удалить.
Ещё теги для роботов: ajax постраничная навигация, ajax пагинация, ajax подгрузка товаров.
58 комментариев
не работает. как в результате должен выглядеть рабочий темплейт?
Шаблон будет выглядеть примерно так.
К сожалению данная конструкция лишь обновляет содержимое блока news-list, а не добавляет новые новости в конец списка. Хотелось бы что бы загрузка работала как во вконтакте.
Значит что-то делаете не так, а точнее ваш JavaScript обновляет содержимое этого блока, а не дописывает в конец.
Привет!
А какой скрипт используется? Какой-то из типовых в ядре или что-то ещё?
Скрипт используется свой. Если нужно, могу выложить.
Буду очень благодарен! И наверняка не я один
Обновил заметку. Добавил пример JavaScript кода. По сути — он универсальный. Единственное условие: надо ссылке, формирующую линк к следующим элементам, добавить атрибут data-wrapper-class со значением класса блока, в который надо грузить загружаемые элементы.
пишет Uncaught TypeError: Cannot read property 'length' of undefined где может быть ошибка?
Fedor, проверил только что на свежем проекте — работает, ошибок нет.
На какой строке у вас ошибка?
Все уже, спасибо, разобрался. А еще вопрос, в каталоге при скролле таким методом возникает задержка секунд шесть, пока подгрузится новый контент. Как возможно ускорить загрузку?
Fedor, в яваскрипт коде задержки нет. Если долго приходит ответ с сервера, значит дело в нём.
Может я чего-то не понимаю. Но в случае с кешированием, мы разделим буфер на ДО и ПОСЛЕ . В итоге получим то что нужно + то что идет дальше по коду. Ведь снизу код не обрезался. По мне может нужно вставить этот коммент еще и после вывода списка и соответственно в list написать так (, $content_html, ). или я что-то упустил?
Дмитрий, с конструкцией list() в заметке всё верно, а вот в случае с html комментарием
я забыл упомянуть, что его нужно вставить и после вывода элементов. Спасибо за наводку :)
Ну да насчет list и как в заметке будет работать. просто для большей наглядности, что код делится на 3 части. А вот меня еще мучает вопрос. Как сделать чтобы при клике на элементе в подгруженном списке и нажатии кнопки назад все вернулось к тому же месту. Допустим пользователь очень много пролистал (подгрузил) ,перешел к какому-нибудь элементу, потом нажимает Назад и попадает снова в начало списка. И опять все сначала. Ломаю голову как такое реализовать по-грамотному)))
Доброго дня! Использовал Ваш скрипт и мне выдало ошибку, что :
Fatal error Call to undefined function htmlspecialcharsbx()
В чем может быть проблема?
Какая версия главного модуля у вас? Функция htmlspecialcharsbx добавлена в версии 11.5.9
Можно попробовать заменить на htmlspecialchars
Использовал ваш скрипт и ваш код но не работает... никаких ошибок, просто все работает как и раньше без этого кода... в чем может быть проблема ?
Алексей, что значит «все работает как и раньше»? Как по вашему должно работать?
я так понимаю при прокрутке вниз должны появляться новые элементы? но этого не происходит
Вариант с включенным кэшированием хорошо. Но не работает при композитном режиме, что актуально на данный момент. Хотелось бы увидеть решение рабочее и для композитного решения.
Владимир, я обновил заметку. Добавил про работу с включенным композитом.
Добрый день!
Попробовала сделать по вашему примеру подгрузку элементов при прокрутке. Но чего-то не получилось.
Ссылки так и остались прежние - в них нет "AJAX_PAGE"
Вот мой код шаблона в компоненте catalog.section
и component_epilog.php
Эхо выводиться, а вот ГЕТ пустой.
Что я не так делаю?
Юлия, мне кажется вы не вникли в принцип работы.
В ссылках элементов не должно быть параметра AJAX_PAGE, он должен быть у ссылки для следующих (которая с классом ajax-pager-link).
Использовали 3-й вариант (с включенным композитным режимом), Подгрузка работает, но почему то после создания кэша композита сбивается кодировка у элементов (видимо кэш создается не в кодировке сайта). Сайт работает в кодировке windows-1251. Почему такое происходит и как можно это исправить?
Владимир, в кеш композита записывает само ядро битрикса. Возможно какой-то баг с этой кодировкой. Напишите им в техническую поддержку, обязательно помогут.
Че за нубы :) Все прекрасно работает,когда помимио тупого копипаста еще и мозги включить!
Здравствуйте! А javascript код вставлять так же в шаблон компонента?:)
Alyona, куда вам удобнее
А какие-то дополнительные настройки компонента news:list нужны? Просто эффекта нуль, вот не знаю в чем дело.
Может кому пригодится, у меня при включенном режиме аякс в настройках компонента как раз происходила некоректная работа данного скрипта (товары не догружались, а обновляли полностью страницу), выключив же ее в настройках все стало ОК. Спасибо автору, все чудесно работает!
Подскажите пожалуйста как быть с таким случаем: https://www.olegpro.ru/post/bitriks_LazyLoad_ili_lenivaya_zagruzka_kontenta_pri_vklyuchennom_keshirovanii.html#c171 Спасибо!
Никита, дописывать js-скрипт.
Сделал 3 вариант (с композитом), в итоге получил не совсем корректную работу: 1. элементы подгружаются при каждом скролле, а не по достижению конца списка элементов. 2. в подгруженных элементах навешенные обработчики ссылок.
Код у нас примерно как у Юлии в комментариях. Используется комплексный компонент Каталог. Код внедряем в catalog.section. Но не работает, вообще ничего не происходит.
Надежда, а что вы ожидаете увидеть?
Распишите подробно по шагам: что делаете и что в каждом шаге ожидаете увидеть.
Вопрос. Как в этом случае можно перезагружать фильтр?
Кирилл, о каком фильтре речь?
Может я туплю конечно ) Щас вот поеду додумывать. Суть какая, есть у меня элементы они выводятся по столько за раз по сколько надо мне. Собственно так же как и в Вашем примере. Но у меня на странице размещен фильтр.... обычный умный фильтр и вот он вчера после реализации подгрузки не работал корректно. Т.е. он не фильтровал элементы. Либо потому что они еще не получены до подгрущки либо еще почему то. Если сталкивались напишите. А так часа через два на работе попробую вникнуть по глубже. Вчера вечером уже не до того было)
Олег Максименко, использую 2-ой способ - с кэшированием. Компонент комплексный, каталог (с фильтрами и прочим), внедряю код формирования ссылки и яваскрипт в шаблон компонента catalog.section, перед foreac и после. В папке с темой для этого компонента создала component_epilog.php с указанным кодом. Затем в адресной строке симулирую ?AJAX_PAGE=Y и скроллю страницу. Элементы выводятся по 6, всего их там 197. Но при скроллинге ничего не происходит. Я еще не проверяла имена массивов и переменных, и вообще сильно не вчитывалась, пока что просто понадеялась, на то, что "Метод будет точно работать в компонентах catalog.section", хотя возможно речь шла о простом, а не о комплексном компоненте. Кстати, у catalog.section есть свой component_epilog.php в корне компонента (а не в теме), наверное, они конфликтуют, надо будет посмотреть, что там написано.
*перед foreach и после. ID с именем news-list присвоила диву, котором перебираются элементы. Вроде ничего не упустила...
при таком варианте в браузере вы должны видеть только «вырезанную» часть страницы, то есть ни шапки, ни подвала быть не должно. Если вы их видите, то ваш component_epilog.php не отрабатывает. В вашем случае он должен быть в папке с шаблоном catalog.section
Олег Максименко, внесла исправления в component_epilog.php в папке с шаблоном catalog.section. Действительно, при параметре AJAX_PAGE=Y, шапки и подвала теперь не видно, идет чистый html компонента, без стилей, без ничего.. А в каталоге по-прежнему ничего при скролле не появляется. Может, я функцию неправильно подключаю? в яваскрипте я не сильна. разве функция должна быть описана в скобках?
Спасибо всем! Все вопросы решил) Все работает) http://tau.zolotarev-studio.ru/services/prodazha/
Надежда, напишите через обратную связь ссылку на сайт, посмотрю.
Олег Максименко, я попробую внедрить то же самое на ЧИСТОЙ установке магазина битрикс, о результатах сообщу.
Олег Максименко, я попробовала. Тут хотя бы консоль ругается: (index):1463 Uncaught ReferenceError: $ is not defined(anonymous function) @ (index):1463(anonymous function) @ (index):1471 Сейчас погуглю, охоже просто на неподключенную библиотеку, хотя она же там должна быть.
написала данные в обратную связь
Как обновить постраничную навигацию ?
Что делать если на странице два компонента? Указанный метод перестает работать. При клике на 1 показать еще, срабатывает и 2
Добавить дополнительных классов блокам. Дописать js-скрипт.
работает только для 2-х страниц. Подгружается долго. По сути этот тот же переход по ссылке только без перезагрузки. Ожидался эффект именно плавной загрузки по одному товар, а не всей 2-й страницы.
отличный компонент! при правильной настройке, работает как надо.
А можно ли при таком варианте исполнения оставлять url в адресной строке? Кажется что это полезнее для seo...
https://www.olegpro.ru/post/bitriks_LazyLoad_ili_lenivaya_zagruzka_kontenta_pri_vklyuchennom_keshirovanii.html#c686 Николай
https://toster.ru/q/6436
К сожалению не работает скрипт, сделал все как указано в примере.
Стандартный компонент и шаблон catalog.section теперь уже и сам умеет в ленивую загрузку. И в композитном режиме и без. Добавляется два параметра при вызове компонента: "LAZY_LOAD"=>"Y" и "LOAD_ON_SCROLL"=>"Y" (обрабатываются на уровне класса ElementList – родителя класса компонента). Код из init.php возвращается в component_epilog.php, а AJAX-запрос вместо самой страницы обращается к файлу ajax.php компонента (который по-умолчанию лежит /bitrix/components/bitrix/catalog.section/ajax.php).
Такой подход несколько сложнее для внедрения, если шаблон переделанный (а если компонент модифицированный, так тем более), есть свои подводные камни, но всё же я лучше не буду лишний раз модифицировать init.php, если этого можно избежать, да и при таком подходе AJAX игнорирует всё, кроме нужного нам компонента, следовательно отработает быстрее. Как минимум обозначит такую возможность всё-таки надо )
Использую по третьему варианту (с композитом) работает, но почему то иногда дублирующийся элементы появляются когда несколько раз кнопку нажимаешь "показать еще". В чем может быть проблема