WordPress LazyLoad Posts - markhowellsmead/helpers GitHub Wiki

This solution uses the REST API to lazy-load posts on demand. The loading in this example takes place when a button is clicked.

Javascript

Load the script file /assets/scripts/lazy-load-posts.min.js in the frontend if there is an element with the data attribute data-load-posts.

(function () {
	if(document.querySelector('[data-load-posts]').length) {
		const lazy_script = document.createElement('script');
		lazy_script.setAttribute('src', shtThemeData.directory_uri + '/assets/scripts/lazy-load-posts.min.js');
		document.head.appendChild(lazy_script);
	}
})();

The content of that file handles the magic.

String.prototype.replaceAll = function (search, replacement) {
	var target = this;
	return target.replace(new RegExp(search, 'g'), replacement);
};

(function ($) {

	var $list,
		$button, $buttonWrapper,
		$api_link = null,
		api_root = null,
		card_template = '',
		pageIndex = 2;

	var appendPosts = function (posts, textStatus, jqXHR) {

		if(parseInt(jqXHR.getResponseHeader('X-WP-TotalPages')) === pageIndex) {
			$buttonWrapper.hide();
		} else {
			$button.removeAttr('disabled');
		}

		pageIndex++;
		var resultCards = '';

		$.each(posts, function () {
			resultCards += card_template.replaceAll('{{ data.id }}', this.id)
				.replaceAll('{{ data.date_c }}', this.date_c)
				.replaceAll('{{ permalink }}', this.link)
				.replaceAll('{{ title }}', this.title.rendered)
				.replaceAll('{{ excerpt }}', this.excerpt && this.excerpt.rendered ? '<div class="c-cardlist__excerpt">' + this.excerpt.rendered + '</div>' : '')
				.replaceAll('{{ thumbnail }}', this.cardlist_image !== '' ? this.cardlist_image : '<div class="c-cardlist__figure c-cardlist__figure--empty"></div>')
				.replaceAll('{{ author_text }}', this.author_text);
		});

		$list.append($(resultCards));

		if($list.data('masonry')) {
			$list.isotope('appended', $resultCards);
			$list.isotope('layout');
			$list.imagesLoaded().progress(function () {
				$list.isotope('layout');
			});
		}

		$(window).trigger('ct::updateequalizer');

	};

	$(document).ready(function () {
		$api_link = $('link[rel="https://api.w.org/"]');
		api_root = $api_link.attr('href');

		card_template = $('[data-post-template]').html();
		$list = $('[data-lazyload-posts]');
		$buttonWrapper = $('[data-load-posts-wrapper]');
		$button = $('[data-load-posts]');
		per_page = $button.data('load-posts');

		if(parseInt($list.data('lazyload-total-posts')) <= parseInt($list.data('lazyload-per-page'))) {
			$button.hide();
		} else {

			$button.on('click.loadposts', function () {

				$(this).blur();
				$(this).attr('disabled', 'disabled');

				var requestData = {
					_embed: true,
					page: pageIndex,
					per_page: per_page,
					lang: $('html').attr('lang').substring(0, 2)
				};

				switch ($list.data('lazyload-termtype')) {
				case 'tag':
					requestData.tags = parseInt($list.data('lazyload-term'));
					break;
				case 'category':
					requestData.categories = parseInt($list.data('lazyload-term'));
					break;
				}

				if(parseInt($list.data('lazyload-per-page'))) {
					requestData.per_page = parseInt($list.data('lazyload-per-page'));
				}

				$.ajax({
					type: "GET",
					dataType: "json",
					url: api_root + 'wp/v2/posts',
					data: requestData,
					success: function (data, textStatus, jqXHR) {
						appendPosts(data, textStatus, jqXHR);
					}
				});
			});
		}

	});

})(jQuery);

PHP

Extend the REST API response

This needs to be modified according to the requirements of the template.

<?php

namespace SayHello\Theme\Package;

use SayHello\Theme\Package\Lazysizes;
use WP_REST_Response;
use WP_Post;
use WP_REST_Request;

class REST
{
	public function run()
	{
		add_action('rest_prepare_post', [$this, 'modifyGetPostData'], 10, 3);
	}

	public function modifyGetPostData(WP_REST_Response $response, WP_Post $post, WP_REST_Request $context)
	{
		if ($post->post_type === 'post') {

			$response->data['date_legible'] = date(get_option('date_format'), strtotime($response->data['date']));
			$response->data['date_c'] = date('c', strtotime($response->data['date']));

			$response->data['author_text'] = sprintf(
				_x('By %s %s', 'Single post view author / date', 'sht'),
				get_the_author_meta('user_nicename', $post->post_author),
				get_the_date('M j Y', $post)
			);

			$response->data['cardlist_image'] = '';
			if (!!$response->data['featured_media']) {
				$response->data['cardlist_image'] = Lazysizes::getLazyImage(
					$response->data['featured_media'],
					'thumbnail-retina',
					'c-cardlist__figure',
					'c-cardlist__image'
				);
			}

			if (!empty($excerpt = $post->post_excerpt)) {
				$response->data['excerpt']['rendered'] = wp_trim_words($excerpt, 60, '&hellip;&nbsp;<a href="'. esc_url(get_permalink()) . '">'. __('Read more &nbsp;&raquo;', 'sht') . '</a>');
			}
		}
		return $response;
	}
}

Template part

The content of the template part partials/news/card-dynamic.php is accessed by JavaScript.

<?php

/**
 * Template used for inline lazyloader in frontend
 */
?>

<li class="c-cardlist__entry">

	{{ thumbnail }}

	<h3 class="c-cardlist__entrytitle"><span class="c-cardlist__entrytitlepre"><?php _ex('News:', 'Card entry title prefix', 'sht');?></span>
		<a href="{{ permalink }}">{{ title }}</a>
	</h3>

	{{ excerpt }}

	<time class="c-cardlist__entrydate" datetime="{{ date_c }}">
		{{ author_text }}
	</time>
</li>

Load the template part file above at the bottom of the cardlist PHP file, thus.

<script type="text/template" data-post-template>
<?php
	get_template_part('partials/news/card-dynamic');
?>
</script>

Modify the <ul class="c-cardlist__entries"> element, thus.

<ul class="c-cardlist__entries" data-lazyload-posts data-lazyload-per-page="<?php echo $GLOBALS['wp_query']->query_vars['posts_per_page'];?>" data-lazyload-total-posts="<?php echo $GLOBALS['wp_query']->found_posts;?>">

Add the loader button within the <div class="c-cardlist__inner"> element.

<?php
if ($GLOBALS['wp_query']->found_posts > $GLOBALS['wp_query']->query_vars['posts_per_page']) {?>
	<div class="c-cardlist__loadmorewrapper" data-load-posts-wrapper>
		<button class="c-cardlist__loadmore" data-load-posts="<?php echo $posts_per_page;?>"><?php _ex('View more', 'Button text to load more posts', 'sht');?></button>
	</div>
<?php }?>
⚠️ **GitHub.com Fallback** ⚠️