I'm looping through custom taxonomy terms to create a WP_Query for each term, then displaying the posts on the front-end.
I'm also attempting to adapt this helpful tutorial(and accompanying comments) to add an ajax load more button to each loop iteration. This is what I believe distinguishes this issue from other posts on WP-StackExchange: adding a load more button to a looped query, rather than a single query or simply multiple hard-coded(non-looped) WP_Query's on one page.
The original tutorial uses wp_localize_script
to pass php data to a registered & enqueued jQuery script, but since I need unique variables created with each loop, it didn't appear that I could use it(and the tutorial author himself says not to use wp_localize_script
if using a custom WP_Query). The comments on that post also allude to the use of unique variables for multiple queries, but I couldn't figure it out for looped queries.
The button that is included in each loop iteration:
// don't display the button if there are not enough posts
if ( $unique_query->max_num_pages > 1 )
echo '<button class="load-more-btn">Load more</button>';
I realize that the following code is not as concise as it could be yet(hence why I'm here), but I'm including in each foreach'd query the same jQuery so I can create unique query vars for each loop (ie. current_page_<?php print $cat_string; ?>
):
The jQuery included at the end of each loop iteration:
$cat_string
= the current taxonomy term string - ie. los_angeles
<script>
var ajaxurl = "<?php print site_url() . '/wp-admin/admin-ajax.php'; ?>",
var posts_<?php print $cat_string; ?> = '<?php echo serialize( $unique_query->query_vars ) ?>',
current_page_<?php print $cat_string; ?> = <?php echo $unique_query->query_vars['paged'] ?>,
max_page_<?php print $cat_string; ?> = <?php echo $unique_query->max_num_pages ?>
jQuery(function($){
$('.load-more-btn').click(function(){
var button = $(this),
data = {
'action': 'loadmore',
'query': posts_<?php print $cat_string; ?>,
'page' : current_page_<?php print $cat_string; ?>
};
$.ajax({
url : ajaxurl, // AJAX handler
data : data,
type : 'POST',
beforeSend : function ( xhr ) {
button.text('Loading...');
},
success : function( data ){
if( data ) {
button.text( 'Load more' ).prev().before(data); // insert new posts
current_page_<?php print $cat_string; ?>++;
if ( current_page_<?php print $cat_string; ?> == max_page_<?php print $cat_string; ?> )
button.remove(); // if last page, remove the button
} else {
/* This seems to be where it jumps currently when the button is clicked */
button.remove(); // if no data, remove the button as well
}
}
});
});
});
</script>
The ajax action loadmore
is hooked in my functions.php like so:
function bos_loadmore_ajax_handler(){
// prepare our arguments for the query
$args = json_decode( stripslashes( $_POST['query'] ), true );
$args['paged'] = $_POST['page'] + 1; // we need next page to be loaded
$args['post_status'] = 'publish';
// it is always better to use WP_Query but not here
query_posts( $args );
if( have_posts() ) :
// run the loop
while( have_posts() ): the_post();
get_template_part( 'template-parts/event', 'small' );
endwhile;
endif;
die; // here we exit the script and even no wp_reset_query() required!
}
add_action('wp_ajax_loadmore', 'bos_loadmore_ajax_handler'); // wp_ajax_{action}
add_action('wp_ajax_nopriv_loadmore', 'bos_loadmore_ajax_handler'); // wp_ajax_nopriv_{action}
The unique query vars are instantiating and populating correctly—I can see the correct output for each tax term loop in the Inspector.
The "Load More" button is shown correctly since the query has 26 posts and is showing 20, but when I click the button it changes to "Loading..." then disappears. From some console.log
's, it seems to jump right to the else branch for "no data found", so I'm thinking the hooked action in my functions file isn't getting the correct query vars to run the query. Does that seem correct?
I can't figure out, though, how the query vars should be different to get the correct query.
And to be thorough, the WP_Query $args for each looped query are:
$paged = ( get_query_var('page') ) ? get_query_var('page') : 1;
$args = array(
'post_type' => array( 'tribe_events' ),
'post_status' => array( 'publish' ),
'posts_per_page' => 20,
'nopaging' => false,
'paged' => $paged,
'tax_query' => array(
array(
'taxonomy' => 'tribe_events_cat',
'field' => 'name',
'terms' => $the_cat,
),
),
);
Any help is much appreciated.
I'm looping through custom taxonomy terms to create a WP_Query for each term, then displaying the posts on the front-end.
I'm also attempting to adapt this helpful tutorial(and accompanying comments) to add an ajax load more button to each loop iteration. This is what I believe distinguishes this issue from other posts on WP-StackExchange: adding a load more button to a looped query, rather than a single query or simply multiple hard-coded(non-looped) WP_Query's on one page.
The original tutorial uses wp_localize_script
to pass php data to a registered & enqueued jQuery script, but since I need unique variables created with each loop, it didn't appear that I could use it(and the tutorial author himself says not to use wp_localize_script
if using a custom WP_Query). The comments on that post also allude to the use of unique variables for multiple queries, but I couldn't figure it out for looped queries.
The button that is included in each loop iteration:
// don't display the button if there are not enough posts
if ( $unique_query->max_num_pages > 1 )
echo '<button class="load-more-btn">Load more</button>';
I realize that the following code is not as concise as it could be yet(hence why I'm here), but I'm including in each foreach'd query the same jQuery so I can create unique query vars for each loop (ie. current_page_<?php print $cat_string; ?>
):
The jQuery included at the end of each loop iteration:
$cat_string
= the current taxonomy term string - ie. los_angeles
<script>
var ajaxurl = "<?php print site_url() . '/wp-admin/admin-ajax.php'; ?>",
var posts_<?php print $cat_string; ?> = '<?php echo serialize( $unique_query->query_vars ) ?>',
current_page_<?php print $cat_string; ?> = <?php echo $unique_query->query_vars['paged'] ?>,
max_page_<?php print $cat_string; ?> = <?php echo $unique_query->max_num_pages ?>
jQuery(function($){
$('.load-more-btn').click(function(){
var button = $(this),
data = {
'action': 'loadmore',
'query': posts_<?php print $cat_string; ?>,
'page' : current_page_<?php print $cat_string; ?>
};
$.ajax({
url : ajaxurl, // AJAX handler
data : data,
type : 'POST',
beforeSend : function ( xhr ) {
button.text('Loading...');
},
success : function( data ){
if( data ) {
button.text( 'Load more' ).prev().before(data); // insert new posts
current_page_<?php print $cat_string; ?>++;
if ( current_page_<?php print $cat_string; ?> == max_page_<?php print $cat_string; ?> )
button.remove(); // if last page, remove the button
} else {
/* This seems to be where it jumps currently when the button is clicked */
button.remove(); // if no data, remove the button as well
}
}
});
});
});
</script>
The ajax action loadmore
is hooked in my functions.php like so:
function bos_loadmore_ajax_handler(){
// prepare our arguments for the query
$args = json_decode( stripslashes( $_POST['query'] ), true );
$args['paged'] = $_POST['page'] + 1; // we need next page to be loaded
$args['post_status'] = 'publish';
// it is always better to use WP_Query but not here
query_posts( $args );
if( have_posts() ) :
// run the loop
while( have_posts() ): the_post();
get_template_part( 'template-parts/event', 'small' );
endwhile;
endif;
die; // here we exit the script and even no wp_reset_query() required!
}
add_action('wp_ajax_loadmore', 'bos_loadmore_ajax_handler'); // wp_ajax_{action}
add_action('wp_ajax_nopriv_loadmore', 'bos_loadmore_ajax_handler'); // wp_ajax_nopriv_{action}
The unique query vars are instantiating and populating correctly—I can see the correct output for each tax term loop in the Inspector.
The "Load More" button is shown correctly since the query has 26 posts and is showing 20, but when I click the button it changes to "Loading..." then disappears. From some console.log
's, it seems to jump right to the else branch for "no data found", so I'm thinking the hooked action in my functions file isn't getting the correct query vars to run the query. Does that seem correct?
I can't figure out, though, how the query vars should be different to get the correct query.
And to be thorough, the WP_Query $args for each looped query are:
$paged = ( get_query_var('page') ) ? get_query_var('page') : 1;
$args = array(
'post_type' => array( 'tribe_events' ),
'post_status' => array( 'publish' ),
'posts_per_page' => 20,
'nopaging' => false,
'paged' => $paged,
'tax_query' => array(
array(
'taxonomy' => 'tribe_events_cat',
'field' => 'name',
'terms' => $the_cat,
),
),
);
Any help is much appreciated.
Share Improve this question edited Dec 26, 2019 at 19:25 dmoz asked Dec 26, 2019 at 19:17 dmozdmoz 1664 silver badges14 bronze badges 3 |1 Answer
Reset to default 0I finally found some helpful information in this post: How to "Load More" posts via AJAX?
The issue ended up being a combination of counting the number of total pages of posts, and sharing that number with the hooked ajax function in functions.php.
Here's my page template loop:
<div class="events">
<?php
$socal_cats = array( 'Los Angeles', 'San Diego', 'Las Vegas' );
foreach( $socal_cats as $the_cat ) {
$cat_slug = str_replace(' ', '-', strtolower($the_cat));
$cat_string = str_replace('-', '_', $cat_slug);
?>
<div id="<?php echo $cat_slug; ?>-events">
<header>
<h2><?php echo $the_cat; ?></h2>
</header>
<div class="events-list">
<?php
// WP_Query arguments
$posts_per_page = 20;
$paged_main = ( get_query_var('page') ) ? get_query_var('page') : 1;
$args = array(
'post_type' => array( 'tribe_events' ),
'post_status' => array( 'publish' ),
'posts_per_page' => $posts_per_page,
'paged' => $paged_main,
'tax_query' => array(
array(
'taxonomy' => 'tribe_events_cat',
'field' => 'name',
'terms' => $the_cat,
),
),
);
// The Query
$socalquery = new WP_Query( $args );
// The Loop
if ( $socalquery->have_posts() ) {
$total_posts = $socalquery->found_posts;
$total_pages = ( ceil( $total_posts / $posts_per_page ) + 1 );
?>
<div class="posts-wrap" id="<?php echo $cat_slug; ?>" data-totalpages="<?php echo $total_pages; ?>">
<?php
while ( $socalquery->have_posts() ) {
$socalquery->the_post();
get_template_part( 'template-parts/event', 'small' );
}
// don't display the button if there are not enough posts
if ( $socalquery->found_posts > $posts_per_page )
echo '<div class="load-more-wrap"><button class="loadmore"><span class="moretext" data-page="'.$total_pages.'">Load more</span></button></div>';
?>
</div><!-- .posts-wrap -->
<?php
} else {
get_template_part( 'template-parts/event', 'none' );
}
?>
</div><!-- .events-list -->
</div><!-- .*category*-events -->
<? } // end foreach ?>
<script type="text/javascript">
// Set starting values which to send from Javascript to WP query function...
var ajaxurl = "<?php echo admin_url( 'admin-ajax.php' ); ?>";
jQuery(function($) {
// When this selector is clicked
$('body').on('click', '.loadmore', function() {
// Get ID of clicked link parent
var clicked_cat = $(this).parent().parent().attr('id');
// Get total pagination pages for this section
var total_pages_for_section = $('#'+clicked_cat+'.posts-wrap').data().totalpages;
// alert(total_pages_for_section);
// Change link text to provide feedback
$('#'+clicked_cat+'.posts-wrap .load-more-wrap .loadmore .moretext').text('Loading...');
$('#'+clicked_cat+'.posts-wrap .load-more-wrap .loadmore .moretext').after('<i class="icon fas fa-sync fa-spin"></i>');
// $('#'+clicked_cat+'.load-more-wrap .loadmore i').attr('class', 'fas fa-cog fa-spin');
// Pick up data-page attribute from the clicked link (defaults to 2 at load)
var clicked_page = $('#'+clicked_cat+'.posts-wrap .load-more-wrap .loadmore').find('span').data().page;
// 1. Send this package of variables to WP query function
var data = {
'action': 'loadmore',
'page': clicked_page, // page of posts to get is the number set in data-page attribute
'clicked_cat': clicked_cat,
'security': '<?php echo wp_create_nonce("load_more_posts"); ?>'
};
// 2. Send to query function and get results
$.post(ajaxurl, data, function(response) {
// Append the returned output to this selector
$(response).insertBefore('#'+clicked_cat+'.posts-wrap .load-more-wrap').fadeIn(500);
// If we have exhausted all post pages, hide the whole "Load More" link
if (clicked_page >= total_pages_for_section) {
$('#'+clicked_cat+'.posts-wrap .load-more-wrap').hide();
// Otherwise, restore it and increment counter
} else {
// Change link text back to original
$('#'+clicked_cat+'.posts-wrap .load-more-wrap .loadmore .moretext').text('Load More');
$('#'+clicked_cat+'.posts-wrap .load-more-wrap .loadmore').find('svg').remove();
// Increment "data-page" attribute of clicked link for next click
$('#'+clicked_cat+'.posts-wrap .load-more-wrap .loadmore').find('span').data().page++;
}
});
});
});
</script>
</div><!-- .events -->
And functions.php:
// Events Load More button
function bos_loadmore_ajax_handler(){
check_ajax_referer('load_more_posts', 'security');
// 1. Query values are passed from referring page, to Javascript and to this query...
$paged = $_POST['page']; // Passed from page: Which page we are on
$clicked_cat = $_POST['clicked_cat']; // ID of the clicked "More" link
switch ($clicked_cat) {
case "los-angeles":
$event_tax_query_term = "los-angeles";
break;
case "san-diego":
$event_tax_query_term = "san-diego";
break;
case "las-vegas":
$event_tax_query_term = "las-vegas";
break;
case "bay-area":
$event_tax_query_term = "bay-area";
break;
case "sacramento":
$event_tax_query_term = "sacramento";
break;
case "reno":
$event_tax_query_term = "reno";
break;
}
// 3. Set query arguments
$args = array(
'post_type' => array( 'tribe_events' ),
'post_status' => 'publish',
'posts_per_page' => 10,
'paged' => $paged,
'tax_query' => array(
array(
'taxonomy' => 'tribe_events_cat',
'field' => 'slug',
'terms' => $event_tax_query_term,
),
),
);
// 4. Query for posts
$query = new WP_Query( $args );
// 5. Send results to Javascript
if ( $query->have_posts() ) :
?>
<?php while ( $query->have_posts() ) : $query->the_post(); ?>
<?php get_template_part( 'template-parts/event', 'small' ); ?>
<?php endwhile; ?>
<?php
endif;
wp_die();
}
add_action('wp_ajax_loadmore', 'bos_loadmore_ajax_handler'); // wp_ajax_{action}
add_action('wp_ajax_nopriv_loadmore', 'bos_loadmore_ajax_handler'); // wp_ajax_nopriv_{action}
Hope this helps someone who needs ajax load more on multiple custom WP_Querys on the same page.
200 status
on the call to admin-ajax.php—when I click the "Load More" button. Seems to be calling that file fine, that's why I still think it's something in the$args
of my 'bos_loadmore_ajax_handler' function. – dmoz Commented Jan 7, 2020 at 16:07