Бесконечная страница: динамическая загрузка контента на WordPress

Все чаще и чаще на в сайты интегрируют элементы SPA, в частности “бесконечную страницу”… здравствуй, читатель! По ходу этой статьи будет описан принцип работы такой страницы, реализация и посадка на вордпресс.

SPA и бесконечная страница

В принципе, и SPA(Single Page Application – одно-страничное приложение) и бесконечная страница исповедуют один и тот же принцип(извиняюсь за тавтологию) – динамическая загрузка контента. Все очень просто – когда поступает событие(клик либо скролл, либо еще что-то), javascript’ом загружается новая порция контента и вставляется в страницу.

Реализация

Использовать буду jQuery(тут часть любителей хайповых React, Vue и прочего, вышли…). Обьясню свой выбор – зачем перегружать страницу яваскриптом? да, jQuery тоже библиотека без которой можно обойтись, но если он используется для многих других элементов – почему бы и нет?

Вот примерная разметка, составленная мной

<div class="container">
    <div class="page-1">
        <div class="card">
            <img src="path/to/img-1" class="img-fluid">
            <a href="path/to/single-post-1">
                <h2>Post Title 1</h2>
            </a>
            <p>post 1 excerpt</p>
        </div>
        ***
    </div>
    <div class="page-2"></div>
    ***
    <template id="post">
        <div class="card">
            <img src="" alt="" class="img-fluid">
            <a href="">
                <h2></h2>
            </a>
        </div>
    </template>
</div>

Немного о ней: поскольку я черпать контент буду в ворпрессе – то и использую пагинацию для разбивки на загружаемые блоки. Первую страницу загрузим обычным способом(каждая страница – это div class="page-%n"). В теге template будет содержаться разметка нашего поста – после загрузки пачки постов я буду ее клонировать.

Далее, нужно прибегнуть к javascript. Как я говорил ранее, на странице будет jQuery, поэтому сделаем AJAX-запрос к WP REST API со списком постов и их данными. Но прежде – определим условия для начала загрузки постов. Раз уж бесконечное полотно – значит условие это достижение нижней границы текущей страницы

var prevScroll = 0,
    page = 1,
    windowH = $(window).height(),
    positionY = $('.page-1').offset().top,
    blockH = $('.page-1').height(),
    template = $('#post').html(),
    currentScroll, tmpl

$(window).scroll(function (event) {
    currentScroll = $(this).scrollTop()

    if (currentScroll > prevScroll) {
        prevScroll = currentScroll

        if (Math.round(blockH + positionY) === (windowH + currentScroll)) {
            // Получаем посты, рисуем их и высчитываем новую границу для загрузки
        }
    }
})

Немного пояснений для этого куска кода. Эта строка(12) позволяет отсеять скролл вверх; можно было б отслеживать прокрутку колеса на мишке – но тогда скрипт был бы мертв на мобильниках. По поводу Math.round() свойства – высота(как и ширина) может быть дробной, в то время как значение скролла всегда целое число. Еще замечу – этот скрипт рабочий только в случаи если под блоком(не по разметке, а по позиции) с постами нет больше блоков(отсутствует футер). Дело в том, что при быстрой прокрутке scrollTop() имеет разрывы и тогда может произойти перескок нужного значения – потому, заменим его(===) простым сравниванием(>). Также, поскольку загрузка постов требует времени, используем коэффициент(что пользователю не пришлось ждать и он не покинул из-за этого сайт). В итоге, строка 15 трансформируется в

if (0.8 * (blockH + positionY) < (windowH + currentScroll)) {

Теперь, перейдем к тому, что внутри блока – содержимому строки 15. Само собой там загрузка и оттрисовка постов, но не только… вот полный код

$(window).off('scroll')
page++

$.get(
    '/wp-json/wp/v2/posts',
    {
        per_page: 5,
        page: page,
        _embed: 'wp:featuredmedia'
    },

    function (response) {
        $(response).each(function (i, e) {
            tmpl = $(template).clone()
            $(tmpl).find('img').attr('src', e._embedded["wp:featuredmedia"][0].source_url)
            $(tmpl).find('a').attr('href', e.link)
            $(tmpl).find('h2').text(e.title.rendered)
            $(tmpl).append(e.excerpt.rendered)

            $('.page-' + page).append(tmpl)
        })

        $('.page-' + page).find('img').on('load', function (i, e) {
            positionY = $('.page-' + page).offset().top
            blockH = $('.page-' + page).height()
        })

        if (page < pagesCount) {
            $(window).on('scroll', onScroll)
        }
    }
)

Традиционно, пояснения:

  • строка 1 “убивает” прослушивание события прокрутки. Это нужно потому, что js асинхронен и способен отослать несколько одинаковых запросов к серверу – что, в свою очередь, приведет к повторению постов(помимо ненужной нагрузки на сервер);
  • строки 4 – 33 сам запрос к серверу: первый аргумент это адрес эндпоинта WP REST API, который возвращает список постов, второй – обьект с параметрами постов, третий – функция, которая будет исполнена при получении ответа;
  • строки 23 – 26 – почему измерения высоты блока заперты в load’е? – у меня нет контейнера с размерами для картинки, а поскольку они грузятся отдельно от аякса – они изменяют размер блока постов через некоторое время;
  • строка 29 возвращаем прослушку прокрутки.

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

В общем, полный код бесконечной страницы(уже посаженой на шаблон страницы вордпрес) можно увидеть и взять здесь.