I 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.