How to Load Posts on Demand using AJAX in Genesis

Posted on

This article is based on Bill Erickson’s excellent tutorial titled Infinite Scroll in WordPress.

We shall add a Load More button below posts on Posts page and Archives in Genesis which when clicked, loads the next set of Posts below the current ones on the page without using a plugin like Jetpack’s infinite scroll module.

Quoting Bill,

The problem with the Jetpack infinite scroll (and other plugins like it) is they have to work with every theme so use a pretty hacky approach. They actually load the next page (like clicking the “Next” link in your pagination), then pick out just the posts in the content area and insert that. You’re loading a whole page when it would be more efficient to only load the posts required.

It can also cause issues with ad networks since you’re inflating your pageviews. Infinite scroll is a “pageview” when Jetpack loads the page to grab the posts, but the ad shown on that page isn’t seen by an actual human.

Finally, with that tool you’re limited to only archive pages since they have the “Next” button Jetpack can press. The approach described above can be used in any instance. When you’re on a single post you can load excerpts of other posts, like I’ve done on Pinch of Yum.

To understand how the solution works, head over to Bill’s article.

I’ve added in a line of logic to ensure that Load More button does not appear when the last set of results is shown.

Step 1

Create a file named load-more.js in child theme’s js directory having the following:

jQuery(function($){

	// $('.post-listing').append( '<button class="load-more">Load More</button>' );
	// var button = $('.post-listing .load-more');
	var page = 2;
	var loading = false;

	$('body').on('click', '.load-more', function(){
		if( ! loading ) {
			loading = true;
			$(this).remove();
			var data = {
				action: 'be_ajax_load_more',
				page: page,
				query: beloadmore.query,
			};
			$.post(beloadmore.url, data, function(res) {
				if( res.success) {
					$('.post-listing').append( res.data );
					// $('.post-listing').hide().append( res.data ).fadeIn(500); // to have fadeIn effect
					// $('.post-listing').append( button );
					page = page + 1;
					loading = false;
				} else {
					// console.log(res);
				}
			}).fail(function(xhr, textStatus, e) {
				// console.log(xhr.responseText);
			});
		}
	});

});

Step 2

Create a file named say, template_load-more.php in the child theme having the following:

/**
* Javascript for Load More
*
*/
function be_load_more_js() {

global $wp_query;
$args = array(
'url' => admin_url( 'admin-ajax.php' ),
'query' => $wp_query->query
);

wp_enqueue_script( 'be-load-more', get_stylesheet_directory_uri() . '/js/load-more.js', array( 'jquery' ), '1.0', true );
wp_localize_script( 'be-load-more', 'beloadmore', $args );

}
add_action( 'wp_enqueue_scripts', 'be_load_more_js' );

add_action( 'genesis_before_loop', 'sk_opening', 20 ); // a high priority of 20 to make this appear below .archive-description
function sk_opening() {
echo '<div class="post-listing">';
}

add_action( 'genesis_after_loop', 'sk_closing' );
function sk_closing() {
// echo '</div>';
echo '<button class="load-more">Load More</button></div>';
}

// Remove Archive Pagination
remove_action( 'genesis_after_endwhile', 'genesis_posts_nav' );

genesis();

Step 3

Add the following in child theme’s functions.php:

/**
* AJAX Load More
* @link http://www.billerickson.net/infinite-scroll-in-wordpress
*/
function be_ajax_load_more() {

$args = isset( $_POST['query'] ) ? array_map( 'esc_attr', $_POST['query'] ) : array();
$args['post_type'] = isset( $args['post_type'] ) ? esc_attr( $args['post_type'] ) : 'post';
$args['paged'] = esc_attr( $_POST['page'] );
$args['post_status'] = 'publish';

ob_start();
$loop = new WP_Query( $args );
if( $loop->have_posts() ): while( $loop->have_posts() ): $loop->the_post();
printf( '<article %s>', genesis_attr( 'entry' ) );

do_action( 'genesis_entry_header' );

do_action( 'genesis_before_entry_content' );

printf( '<div %s>', genesis_attr( 'entry-content' ) );
do_action( 'genesis_entry_content' );
echo '</div>';

do_action( 'genesis_after_entry_content' );

do_action( 'genesis_entry_footer' );

echo '</article>';
// endwhile; endif; wp_reset_postdata();
endwhile; if ( $_POST['page'] < $loop->max_num_pages ) { echo '<button class="load-more">Load More</button>'; } endif; wp_reset_postdata();
$data = ob_get_clean();
wp_send_json_success( $data );
wp_die();
}
add_action( 'wp_ajax_be_ajax_load_more', 'be_ajax_load_more' );
add_action( 'wp_ajax_nopriv_be_ajax_load_more', 'be_ajax_load_more' );

/**
* Template Redirect
* Use template_load-more.php for Posts page and all archives.
*/
add_filter( 'template_include', 'custom_blog_archives_template', 99 );
function custom_blog_archives_template( $template ) {

if ( is_home() || is_archive() ) {
$new_template = locate_template( array( 'template_load-more.php' ) );
if ( '' != $new_template ) {
return $new_template ;
}
}

return $template;
}

Warning: Use of Template Redirect as per the above code block will render home.php, archive.php, category.php and any other files that honour the WordPress template hierarchy (if present) ineffective. If usage of such templates is necessary, consider using get_template_part() to include the code of template_load-more.php in those individual files.

Step 4

Add the following in child theme’s style.css:

.post-listing {
	margin-bottom: 40px;
}

.load-more {
	display: table;
	margin: 0 auto;
}

References:

genesis/lib/structure/loops.php