Solution:1
This is how I load new posts onto the same page using the existing custom WP_Query()
.
This method also works even with custom url vars which modify the WP_Query()
.
See my comments in code provided below.
Here is the query php with loops and load more button…
<?php
// exclude post id's (default false)
$excludePosts = isset($_REQUEST['excludePosts']) ? $_REQUEST['excludePosts'] : false;
// posts per page and load more posts to add
$postsPerPage = 20;
// products query args
$args = [
'post_type' => 'products',
'post_status' => 'publish',
'orderby' => 'ID',
'order' => 'DESC',
'posts_per_page' => $postsPerPage,
// here is the trick, exclude current posts from query when using jQuery $.post()
'post__not_in' => $excludePosts
];
// products query
$products = new WP_Query($args);
?>
<?php // we need this, a parent container for products ?>
<div id="products_archive">
<?php // if we have query results ?>
<?php if( $products->have_posts() ): ?>
<?php // loop our query results (products) ?>
<?php while( $products->have_posts()): $products->the_post() ?>
<?php // your product with a data-product attribute and ID as the value ?>
<?php // we need data-product to find all the currently loaded product ID's ?>
<article data-product="<?=get_the_id()?>">
<?php the_title(); ?>
<?php the_content(); ?>
</article>
<?php endwhile; ?>
<?php // if we have more posts to load based on current query, then show button ?>
<?php if( $products->max_num_pages > 1 ): ?>
<button id="load_more_products">
Load more products
</button>
<?php endif; ?>
<?php // if we have no query results ?>
<?php else: ?>
Sorry no products found.
<?php endif; ?>
</div>
<?php // we need this, to pass the posts per page value to external jQuery script ?>
<script>
window.postsPerPage = '<?=$postsPerPage?>';
</script>
Here is the jQuery script using $.post()
to reload the current page query excluding products which have already loaded.
// load more products button click event
$(document).on('click', '#load_more_products', function() {
// our click button
let $btn = $(this);
// disable button (you can add spinner to button here too)
$btn.prop('disabled', true);
// set current products empty array
let currentProducts = [];
// loop thought trough all products in products in archive
$('[data-product]', '#products_archive').each( function(key, elem) {
// current product push elements to current products array
currentProducts.push( $(elem).data('product') );
});
// re post current page running the query again
$.post('', {
// pass this data to our php var $excludePosts to modify the page WP_Query
excludePosts: currentProducts
// html param passed data is full html of our reloaded page
}, function(html) {
// find all the product article elements via data-product attribute in html
let $newProducts = $('#products_archive [data-product]', html);
// if new products length is less than our window.postsPerPage variable value
if( $newProducts.length < postsPerPage ) {
// then hide the load more button
$btn.hide();
}
// insert new products after last current data-product article
$newProducts.insertAfter( $('[data-product]', '#products_archive').last() );
})
// once our new products have been inserted
.done(function() {
// do stuff here to lazy load images etc...
// re-enable button (and stop button spinner if you have one)
$btn.prop('disabled', false);
});
});
Depending on the structure of your current HTML and the placement/appendage of newly fetched products html, you will need to adjust script to suit.