В этой заметке я хочу рассказать о технологии LazyLoad или в простонародье «ленивая загрузка»(её ещё называют бесконечный скроллинг или Infinity Scroll) в Битриксе. Метод, описанный мной, будет точно работать в компонентах catalog.section и news.list.

Файлы компонентов, мы конечно же, модифицировать не будем.

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

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

Технические детали

В конце, после списка элементов, мы будем вставлять пустую ссылку, на следующие элементы. В ссылку будем добавлять параметр AJAX_PAGE, он нам ещё понадобится.
Выглядеть она будет примерно так:

/news/?PAGEN_2=2&AJAX_PAGE=Y

Формировать ссылку будем следующим образом:

<?php
$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 подгрузка товаров.