AJAX загрузка постов в WordPress: кнопка и бесконечный скролл

AJAX-подгрузка постов в WordPress — это удобный способ динамически отображать контент без перезагрузки страницы. Такая функциональность особенно подойдет блогам, новостным лентам, каталогам товаров и проектам, где пользователю нужно быстро просматривать большое количество записей. Благодаря AJAX можно повысить удобство и скорость взаимодействия с сайтом, а также улучшить визуальное восприятие.

В этой статье рассмотрим 2 способа как реализовать подгрузку постов с помощью AJAX в WordPress:

  1. при клике на кнопку «Показать еще»
  2. при скролле страницы

Способ #1

Загрузка постов по кнопке «Загрузить еще»

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

Так как мы будем работать с основным запросом WP_Query, то количество постов, которые будут подгружаться можно изменить в админке WordPress в разделе Настройки > Чтение.

Добавляем кнопку

В шаблоне, где выводятся посты (index.php, archive.php, home.php) нужно добавить кнопку «Загрузить еще». Кнопка должна быть расположена за пределами цикла вывода постов while.

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

<div class="articles">
    <div class="articles__wrapper">
        <?php if (have_posts()) { ?>
            <div class="articles__list">
                <?php while (have_posts()) {
                    the_post();
                    get_template_part('template-parts/article');
                } ?>
            </div>
        <?php } ?>
        <?php
        global $wp_query;
        $max_pages = $wp_query->max_num_pages;
        $page = get_query_var('paged') ? get_query_var('paged') : 1;
        ?>
        <?php if ($max_pages > $page) { ?>
            <div class="articles__load">
                <button type="button" class="button button--blue" data-page="<?php echo esc_attr( $page ) ?>" id="load-more">Загрузить еще</button>
            </div>
        <?php } ?>
    </div>
</div>

Это приблизительный код, который выводит кнопку после цикла постов. Строки 3-10 — вывод постов в цикле, строки 16-20 – вывод разметки кнопки с дополнительной проверкой. Кнопке добавили id="load-more", это нам понадобится в js обработчике.

В атрибуте кнопки data-page передаем номер текущей страницы, чтобы ajax-загрузка на страницах пагинации работала правильно.

Создание и подключение JS файла

Создадим новый js файл, который будет содержать весь javascript код, который относится к ajax загрузке постов.

Я создал файл load-posts.js в папке js шаблона. Для правильного подключения воспользуемся функцией wp_enqueue_script().

В файле functions.php добавьте код:

add_action('wp_enqueue_scripts', 'enqueue_scripts_load_posts');

function enqueue_scripts_load_posts()
{

	wp_enqueue_script(
		'load_posts',
		get_stylesheet_directory_uri() . '/js/load-posts.js',
		['jquery'],
		null,
		[
			'in_footer' => true
		]
	);
}

Если вы используете шаблон, который часто обновляется, для внесения изменений в файл воспользуйтесь дочерней темой.

JavaScript обработчик

Пришло время написать обработчик события клика на кнопку #load-more. Открываем файл load-posts.js и добавляем код:

(function($) {
    $(document).ready(function() {
        $('#load-more').on('click', function() {
            const ajaxurl = '/wp-admin/admin-ajax.php';
            let button = $(this);
            let page = button.data('page');
    
            $.ajax({
                url: ajaxurl,
                type: 'POST',
                data: {
                    action: 'load_posts',
                    page: page
                },
                beforeSend: function() {
                    button.text('Загрузка...');
                },
                success: function(response) {
                    if (response.trim() !== '') {
                        $('.articles__list').append(response);
                        button.attr('data-page', page + 1);
                        button.text('Загрузить ещё');
                    } else {
                        button.remove();
                    }
                }
            });
        });
    });
})(jQuery)

По клику на кнопку отправляем AJAX запрос на загрузку постов, если есть контент, то посты добавляются в контейнер .articles__list (укажите свой) и изменяется текущий номер страницы. Если постов к загрузке нет – кнопка удаляется.

В beforeSend добавили промежуточное поведение для кнопки. Пока выполняется запрос, текст кнопки поменяется на Загрузка...

PHP обработчик

Остался последний шаг. Необходимо написать PHP-обработчик для AJAX запроса.

Все ajax запросы в WordPress принято направлять в файл /wp-admin/admin-ajax.php. Благодаря этому очень легко обрабатывать запросы через специальные динамические хуки wp_ajax_{$action} и wp_ajax_nopriv_{$action}.

Откройте файл functions.php и добавьте код:

add_action('wp_ajax_load_posts', 'ajax_load_posts');
add_action('wp_ajax_nopriv_load_posts', 'ajax_load_posts');

function ajax_load_posts() {
    $paged = isset($_POST['page']) ? intval($_POST['page']) : 1;

    $query = new WP_Query([
        'post_type' => 'post',
        'paged' => $paged,
        'post_status' => 'publish'
    ]);

    if ($query->have_posts()) {
        while ($query->have_posts()) : $query->the_post();
            get_template_part('template-parts/article');
        endwhile;
    } else {
        echo '';
    }

    wp_reset_postdata();
    wp_die();
}

Так как мы работаем с постами, в параметрах WP_Query достаточно указать post_type, post_status и обязательно указать номер страницы (paged), остальные параметры подтянутся автоматически.

Для этого кода есть важное уточнени. В строке 14 используется функция get_template_part которая подгружает файл разметки одного поста. В вашем шаблоне может не использоваться такой файл, а все разметка находится в файле index.php или archive.php. В таком случае, вам необходимо встроить html-код прямо внутрь обработчика и использовать буферизацию.

Вот как это можно сделать:

add_action('wp_ajax_load_posts', 'ajax_load_posts');
add_action('wp_ajax_nopriv_load_posts', 'ajax_load_posts');

function ajax_load_posts() {
    $paged = isset($_POST['page']) ? intval($_POST['page']) : 1;

    $query = new WP_Query([
        'post_type' => 'post',
        'paged' => $paged,
        'post_status' => 'publish'
    ]);

    ob_start(); // Запуск буфера вывода
    if ($query->have_posts()) {
        while ($query->have_posts()) : $query->the_post();
            ?>
            <div class="article">
                <a href="<?php echo get_the_permalink() ?>" class="article__name"><?php echo get_the_title() ?></a>
                <div class="article__desc"><?php echo get_the_excerpt() ?></div>
                <a href="<?php echo get_the_permalink() ?>" class="article__more">Читать далее</a>
            </div>
            <?php 
        endwhile;
    } else {
        echo '';
    }

    echo ob_get_clean(); // Вывод содержимого буфера

    wp_reset_postdata();
    wp_die();
}
  • Готово! Теперь можно проверить, как работает загрузка.

Способ #2

AJAX загрузка постов при скролле

Теперь реализуем бесконечный скролл страницы с записями. «Бесконечный» он до того момента, пока не подгрузятся все записи 🙂

Кнопка нам больше не понадобится, вместо нее нужно добавить специальный невидимый блок-якорь, при промотке к которому, посты начнут автоматически загружаться.

Многие реализации бесконечного скролла постов в WordPress используют промотку до конца страницы, но такой способ может не сработать, если, например, после блока с постами выводятся какие-то другие блоки. В этом случае как раз подходит способ, который я рассмотрю здесь.

Разметка HTML

<div class="articles">
    <div class="articles__wrapper">
        <?php if (have_posts()) { ?>
            <div class="articles__list">
                <?php while (have_posts()) {
                    the_post();
                    get_template_part('template-parts/article');
                } ?>
            </div>
        <?php } ?>
        <?php
        global $wp_query;
        $max_pages = $wp_query->max_num_pages;
        $page = get_query_var('paged') ? get_query_var('paged') : 1;
        ?>
        <?php if ($max_pages > $page) { ?>
            <div class="articles__load">
                <div id="ajax-load-trigger" data-page="<?php echo esc_attr( $page ) ?>" data-max-page="<?php echo esc_attr( $max_pages ) ?>"></div>
            </div>
        <?php } ?>
    </div>
</div>

Обратите внимание, вместо кнопки мы используем блок #ajax-load-trigger. В data- параметрах передаем номер текущей страницы и максимальное количество страниц.

PHP-обработчик

Код php остается абсолютно таким же, как в предыдущей реализации. Копируем его в functions.php.

JS-обработчик

А вот код JS будет другой. Будем использовать API IntersectionObserver, который отслеживает пересечение элемента другим элементом или областью видимости страницы.

(function ($) {
    let $trigger = $('#ajax-load-trigger'),
        page = Number($trigger.attr('data-page')),
        maxPage = $trigger.attr('data-max-page'),
        loading = false;

    if ($trigger.length) {
        let observeParams = {
            rootMargin: '0px',
            threshold: 1.0
        };

        let ajaxTriggerLoad = function (entries, observer) {
            entries.forEach(function (entry) {
                if (entry.isIntersecting && !loading) {
                    const ajaxurl = '/wp-admin/admin-ajax.php';
                    loading = true;
                    $.ajax({
                        url: ajaxurl,
                        type: 'POST',
                        data: {
                            action: 'load_posts',
                            page: page+1
                        },
                        beforeSend: function () {
                            $trigger.text('Загрузка...');
                        },
                        success: function (response) {
                            if (response.trim() !== '') {
                                $('.articles__list').append(response);
                                page++;
                                loading = false;
                            }

                            if (page == maxPage) {
                                observer.unobserve($trigger[ 0 ]);
                                $trigger.remove();
                            }
                        }
                    });
                }
            });
        }

        let observerAjaxTriggerLoad = new IntersectionObserver(ajaxTriggerLoad, observeParams);
        observerAjaxTriggerLoad.observe($trigger[ 0 ]);
    }
})(jQuery)

IntersectionObserver следит за нашим якорным элементом #ajax-load-trigger. Как только он пересекает нижнюю границу экрана, запустится AJAX загрузка постов и появится временный текст «Загрузка...».

При успешной загрузке записи добавляются на страницу. Если все записи загрузились, то якорный элемент удаляется из DOM и загрузка больше не происходит.

Добавьте код в файл load-posts.js, который мы создавали выше, сохраните все изменения файлов и можно проверять как это работает.

Если вы все сделали по инструкции, то все должно работать. При возникновении сложностей и дополнительных вопросов пишите в комментариях.