I have multiple wp_query's on the same page. Each one of those queries has an AJAX "read more" button at the bottom. The problem I'm finding is that I can only get one to work at a time. Whichever function is added first in the functions.php, that one works - the other one gets a 403 error for admin-ajax.php.
I'm pretty new to AJAX, so I've probably made a total hash of it, and I'm sure there's a way to combine this into a single function (preferred!) but if I can even just find a way for them all to work independently, that work be fine.
Here's my code:
The 2 WP_Queries in a custom page-work.php template:
First one:
<?php
//Find out how many posts
$total_posts = wp_count_posts('smart_maps');
$total_posts = $total_posts->publish;
$number_shown = 0;
$the_query = new WP_Query( $args ); ?>
<?php if ( $the_query->have_posts() ) : ?>
<div id="smartmaps" class="smartmaps col xs-col-14 xs-offset-1 md-col-12 md-offset-2" style="display:none;">
<div class="in-here-smartmaps">
<?php while ( $the_query->have_posts() ) : $the_query->the_post(); ?>
<a href="<?php echo get_permalink(get_the_ID());?>">
<div class="single-smartmap col xs-col-16 md-col-8">
<div class="col xs-col-8 xs-offset-4 md-col-4 md-offset-0 center">
<div class="image-element">
<img src="<?php the_field('thumbnail_image');?>" class="circle">
<div class="image-overlay circle"></div>
</div>
</div>
<div class="col xs-col-14 xs-offset-1 md-col-10 md-offset-1">
<h5 class="smart-map-title"><?php the_title();?></h5>
<?php if(get_field('icons')) :?>
<div class="block-icons">
<?php if(in_array('Heart', get_field('icons'))) :?>
<span class="icon-heart"></span>
<?php endif;?>
<?php if(in_array('Plant', get_field('icons'))) :?>
<span class="icon-plant"></span>
<?php endif; ?>
<?php if(in_array('Cup', get_field('icons'))) :?>
<span class="icon-cup"></span>
<?php endif;?>
<?php if(in_array('Book', get_field('icons'))) :?>
<span class="icon-book"></span>
<?php endif;?>
</div>
<?php endif;?>
</div>
</div>
</a>
<?php $number_shown++;
endwhile; ?>
<?php endif;?>
</div>
<?php if($number_shown != $total_posts) :?>
<div class="loadmore smarties col xs-col-14 xs-offset-1 md-col-8 md-offset-4 center">
<h3><a href="#">LOAD MORE</a></h3>
</div>
<?php endif;?>
</div>
<div clas="clearfix"></div>
The second 1:
<div id="strategic-events" class="strategicevents col xs-col-14 xs-offset-1 md-col-12 md-offset-2" style="display:none;">
<div class="in-here-strats">
<?php
//Find out how many posts
$total_posts = wp_count_posts('strategic_events');
$total_posts = $total_posts->publish;
$number_shown = 0;
$args = array( 'post_type' => 'strategic_events', 'posts_per_page' => 10, 'offset' => $the_offset );
$the_query2 = new WP_Query( $args ); ?>
<?php if ( $the_query2->have_posts() ) :
while ( $the_query2->have_posts() ) : $the_query2->the_post(); ?>
<a href="<?php echo get_permalink(get_the_ID());?>">
<div class="single-strategicevent col xs-col-16 md-col-8">
<div class="col xs-col-8 xs-offset-4 md-col-4 md-offset-0 center">
<div class="image-element">
<img src="<?php the_field('strategic_event_image');?>" class="circle">
<div class="image-overlay circle"></div>
</div>
</div>
<div class="col xs-col-14 xs-offset-1 md-col-10 md-offset-1">
<h5 class="strategic-event-title"><?php the_title();?></h5>
<?php if(get_field('subtitle')) :?>
<p><?php the_field('subtitle');?></p>
<small><?php the_field('location_text');?></small>
<?php endif;?>
</div>
</div>
</a>
<?php $number_shown++;
endwhile;
endif; ?>
</div>
<?php if($number_shown != $total_posts) :?>
<div class="loadmore strats col xs-col-14 xs-offset-1 md-col-8 md-offset-4 center">
<h3><a href="#">LOAD MORE</a></h3>
</div>
<?php endif;?>
</div>
<div class="clearfix"></div>
My JS code (both also in the page-work.php for now)...
<script>
var ajaxurl = "<?php echo admin_url( 'admin-ajax.php' ); ?>";
var page = 2;
jQuery(function($) {
$('body').on('click', '.loadmore.strats', function(e) {
e.preventDefault();
var data = {
'action': 'load_posts_by_ajax',
'page': page,
'security': '<?php echo wp_create_nonce("load_strats_posts"); ?>',
'max_page': '<?php global $wp_query; echo $wp_query->max_num_pages;?>'
};
$.post(ajaxurl, data, function(response) {
$('.in-here-strats').append(response);
page++;
});
});
});
<script>
var ajaxurl = "<?php echo admin_url( 'admin-ajax.php' ); ?>";
var page = 2;
jQuery(function($) {
$('body').on('click', '.loadmore.smarties', function(e) {
e.preventDefault();
var data = {
'action': 'load_posts_by_ajax',
'page': page,
'security2': '<?php echo wp_create_nonce("load_smartmaps_posts"); ?>',
'max_page': '<?php global $wp_query; echo $wp_query->max_num_pages;?>'
};
$.post(ajaxurl, data, function(response) {
$('.in-here-smartmaps').append(response);
page++;
});
});
});
</script>
And the AJAX functions in my function.php:
//Load More Smart Maps
add_action('wp_ajax_load_posts_by_ajax', 'load_smart_maps_by_ajax_callback');
add_action('wp_ajax_nopriv_load_posts_by_ajax', 'load_smart_maps_by_ajax_callback');
function load_smart_maps_by_ajax_callback() {
check_ajax_referer('load_smartmaps_posts', 'security2');
$paged = $_POST['page'];
$args = array(
'post_type' => 'smart_maps',
'post_status' => 'publish',
'posts_per_page' => '10',
'paged' => $paged,
);
$my_posts = new WP_Query( $args );
if ( $my_posts->have_posts() ) :
?>
<?php
$total_posts = wp_count_posts('smart_maps');
$total_posts = $total_posts->publish;
$number_shown = $paged * 10 - 10;?>
<?php while ( $my_posts->have_posts() ) : $my_posts->the_post() ?>
<a href="<?php echo get_permalink(get_the_ID());?>">
<div class="single-smartmap col xs-col-16 md-col-8">
<div class="col xs-col-8 xs-offset-4 md-col-4 md-offset-0 center">
<div class="image-element">
<img src="<?php the_field('thumbnail_image');?>" class="circle">
<div class="image-overlay circle"></div>
</div>
</div>
<div class="col xs-col-14 xs-offset-1 md-col-10 md-offset-1">
<h5 class="smart-map-title"><?php the_title();?></h5>
<?php if(get_field('icons')) :?>
<div class="block-icons">
<?php if(in_array('Heart', get_field('icons'))) :?>
<span class="icon-heart"></span>
<?php endif;?>
<?php if(in_array('Plant', get_field('icons'))) :?>
<span class="icon-plant"></span>
<?php endif; ?>
<?php if(in_array('Cup', get_field('icons'))) :?>
<span class="icon-cup"></span>
<?php endif;?>
<?php if(in_array('Book', get_field('icons'))) :?>
<span class="icon-book"></span>
<?php endif;?>
</div>
<?php endif;?>
</div>
</div>
</a>
<?php $number_shown++;
if ( $number_shown == $total_posts ) {
echo '<style>.loadmore.smarties {display:none;}</style>';
}
endwhile ?>
<?php endif;
wp_die();
}
//Load More Strategic Events
add_action('wp_ajax_load_posts_by_ajax', 'load_strats_by_ajax_callback');
add_action('wp_ajax_nopriv_load_posts_by_ajax', 'load_strats_by_ajax_callback');
function load_strats_by_ajax_callback() {
check_ajax_referer('load_strats_posts', 'security');
$paged = $_POST['page'];
$args = array(
'post_type' => 'strategic_events',
'post_status' => 'publish',
'posts_per_page' => '10',
'paged' => $paged,
);
$my_posts = new WP_Query( $args );
if ( $my_posts->have_posts() ) :
?>
<?php
$total_posts = wp_count_posts('strategic_events');
$total_posts = $total_posts->publish;
$number_shown = $paged * 10 - 10;?>
<?php while ( $my_posts->have_posts() ) : $my_posts->the_post() ?>
<a href="<?php echo get_permalink(get_the_ID());?>">
<div class="single-strategicevent col xs-col-16 md-col-8">
<div class="col xs-col-8 xs-offset-4 md-col-4 md-offset-0 center">
<div class="image-element">
<img src="<?php the_field('strategic_event_image');?>" class="circle">
<div class="image-overlay circle"></div>
</div>
</div>
<div class="col xs-col-14 xs-offset-1 md-col-10 md-offset-1">
<h5 class="strategic-event-title"><?php the_title();?></h5>
<?php if(get_field('subtitle')) :?>
<p><?php the_field('subtitle');?></p>
<small><?php the_field('location_text');?></small>
<?php endif;?>
</div>
</div>
</a>
<?php $number_shown++;
if ( $number_shown == $total_posts ) {
echo '<style>.loadmore.strats {display:none;}</style>';
}
endwhile ?>
<?php endif;
wp_die();
}
I'm all up for simplifying this into a single function if possible, or if not, at least getting it to work independently. I'm guessing that it's only viewing 1 wp_nonce's and checking it against both AJAX calls, so the first one it reads is fine, the second is coming up with a 403?
I have multiple wp_query's on the same page. Each one of those queries has an AJAX "read more" button at the bottom. The problem I'm finding is that I can only get one to work at a time. Whichever function is added first in the functions.php, that one works - the other one gets a 403 error for admin-ajax.php.
I'm pretty new to AJAX, so I've probably made a total hash of it, and I'm sure there's a way to combine this into a single function (preferred!) but if I can even just find a way for them all to work independently, that work be fine.
Here's my code:
The 2 WP_Queries in a custom page-work.php template:
First one:
<?php
//Find out how many posts
$total_posts = wp_count_posts('smart_maps');
$total_posts = $total_posts->publish;
$number_shown = 0;
$the_query = new WP_Query( $args ); ?>
<?php if ( $the_query->have_posts() ) : ?>
<div id="smartmaps" class="smartmaps col xs-col-14 xs-offset-1 md-col-12 md-offset-2" style="display:none;">
<div class="in-here-smartmaps">
<?php while ( $the_query->have_posts() ) : $the_query->the_post(); ?>
<a href="<?php echo get_permalink(get_the_ID());?>">
<div class="single-smartmap col xs-col-16 md-col-8">
<div class="col xs-col-8 xs-offset-4 md-col-4 md-offset-0 center">
<div class="image-element">
<img src="<?php the_field('thumbnail_image');?>" class="circle">
<div class="image-overlay circle"></div>
</div>
</div>
<div class="col xs-col-14 xs-offset-1 md-col-10 md-offset-1">
<h5 class="smart-map-title"><?php the_title();?></h5>
<?php if(get_field('icons')) :?>
<div class="block-icons">
<?php if(in_array('Heart', get_field('icons'))) :?>
<span class="icon-heart"></span>
<?php endif;?>
<?php if(in_array('Plant', get_field('icons'))) :?>
<span class="icon-plant"></span>
<?php endif; ?>
<?php if(in_array('Cup', get_field('icons'))) :?>
<span class="icon-cup"></span>
<?php endif;?>
<?php if(in_array('Book', get_field('icons'))) :?>
<span class="icon-book"></span>
<?php endif;?>
</div>
<?php endif;?>
</div>
</div>
</a>
<?php $number_shown++;
endwhile; ?>
<?php endif;?>
</div>
<?php if($number_shown != $total_posts) :?>
<div class="loadmore smarties col xs-col-14 xs-offset-1 md-col-8 md-offset-4 center">
<h3><a href="#">LOAD MORE</a></h3>
</div>
<?php endif;?>
</div>
<div clas="clearfix"></div>
The second 1:
<div id="strategic-events" class="strategicevents col xs-col-14 xs-offset-1 md-col-12 md-offset-2" style="display:none;">
<div class="in-here-strats">
<?php
//Find out how many posts
$total_posts = wp_count_posts('strategic_events');
$total_posts = $total_posts->publish;
$number_shown = 0;
$args = array( 'post_type' => 'strategic_events', 'posts_per_page' => 10, 'offset' => $the_offset );
$the_query2 = new WP_Query( $args ); ?>
<?php if ( $the_query2->have_posts() ) :
while ( $the_query2->have_posts() ) : $the_query2->the_post(); ?>
<a href="<?php echo get_permalink(get_the_ID());?>">
<div class="single-strategicevent col xs-col-16 md-col-8">
<div class="col xs-col-8 xs-offset-4 md-col-4 md-offset-0 center">
<div class="image-element">
<img src="<?php the_field('strategic_event_image');?>" class="circle">
<div class="image-overlay circle"></div>
</div>
</div>
<div class="col xs-col-14 xs-offset-1 md-col-10 md-offset-1">
<h5 class="strategic-event-title"><?php the_title();?></h5>
<?php if(get_field('subtitle')) :?>
<p><?php the_field('subtitle');?></p>
<small><?php the_field('location_text');?></small>
<?php endif;?>
</div>
</div>
</a>
<?php $number_shown++;
endwhile;
endif; ?>
</div>
<?php if($number_shown != $total_posts) :?>
<div class="loadmore strats col xs-col-14 xs-offset-1 md-col-8 md-offset-4 center">
<h3><a href="#">LOAD MORE</a></h3>
</div>
<?php endif;?>
</div>
<div class="clearfix"></div>
My JS code (both also in the page-work.php for now)...
<script>
var ajaxurl = "<?php echo admin_url( 'admin-ajax.php' ); ?>";
var page = 2;
jQuery(function($) {
$('body').on('click', '.loadmore.strats', function(e) {
e.preventDefault();
var data = {
'action': 'load_posts_by_ajax',
'page': page,
'security': '<?php echo wp_create_nonce("load_strats_posts"); ?>',
'max_page': '<?php global $wp_query; echo $wp_query->max_num_pages;?>'
};
$.post(ajaxurl, data, function(response) {
$('.in-here-strats').append(response);
page++;
});
});
});
<script>
var ajaxurl = "<?php echo admin_url( 'admin-ajax.php' ); ?>";
var page = 2;
jQuery(function($) {
$('body').on('click', '.loadmore.smarties', function(e) {
e.preventDefault();
var data = {
'action': 'load_posts_by_ajax',
'page': page,
'security2': '<?php echo wp_create_nonce("load_smartmaps_posts"); ?>',
'max_page': '<?php global $wp_query; echo $wp_query->max_num_pages;?>'
};
$.post(ajaxurl, data, function(response) {
$('.in-here-smartmaps').append(response);
page++;
});
});
});
</script>
And the AJAX functions in my function.php:
//Load More Smart Maps
add_action('wp_ajax_load_posts_by_ajax', 'load_smart_maps_by_ajax_callback');
add_action('wp_ajax_nopriv_load_posts_by_ajax', 'load_smart_maps_by_ajax_callback');
function load_smart_maps_by_ajax_callback() {
check_ajax_referer('load_smartmaps_posts', 'security2');
$paged = $_POST['page'];
$args = array(
'post_type' => 'smart_maps',
'post_status' => 'publish',
'posts_per_page' => '10',
'paged' => $paged,
);
$my_posts = new WP_Query( $args );
if ( $my_posts->have_posts() ) :
?>
<?php
$total_posts = wp_count_posts('smart_maps');
$total_posts = $total_posts->publish;
$number_shown = $paged * 10 - 10;?>
<?php while ( $my_posts->have_posts() ) : $my_posts->the_post() ?>
<a href="<?php echo get_permalink(get_the_ID());?>">
<div class="single-smartmap col xs-col-16 md-col-8">
<div class="col xs-col-8 xs-offset-4 md-col-4 md-offset-0 center">
<div class="image-element">
<img src="<?php the_field('thumbnail_image');?>" class="circle">
<div class="image-overlay circle"></div>
</div>
</div>
<div class="col xs-col-14 xs-offset-1 md-col-10 md-offset-1">
<h5 class="smart-map-title"><?php the_title();?></h5>
<?php if(get_field('icons')) :?>
<div class="block-icons">
<?php if(in_array('Heart', get_field('icons'))) :?>
<span class="icon-heart"></span>
<?php endif;?>
<?php if(in_array('Plant', get_field('icons'))) :?>
<span class="icon-plant"></span>
<?php endif; ?>
<?php if(in_array('Cup', get_field('icons'))) :?>
<span class="icon-cup"></span>
<?php endif;?>
<?php if(in_array('Book', get_field('icons'))) :?>
<span class="icon-book"></span>
<?php endif;?>
</div>
<?php endif;?>
</div>
</div>
</a>
<?php $number_shown++;
if ( $number_shown == $total_posts ) {
echo '<style>.loadmore.smarties {display:none;}</style>';
}
endwhile ?>
<?php endif;
wp_die();
}
//Load More Strategic Events
add_action('wp_ajax_load_posts_by_ajax', 'load_strats_by_ajax_callback');
add_action('wp_ajax_nopriv_load_posts_by_ajax', 'load_strats_by_ajax_callback');
function load_strats_by_ajax_callback() {
check_ajax_referer('load_strats_posts', 'security');
$paged = $_POST['page'];
$args = array(
'post_type' => 'strategic_events',
'post_status' => 'publish',
'posts_per_page' => '10',
'paged' => $paged,
);
$my_posts = new WP_Query( $args );
if ( $my_posts->have_posts() ) :
?>
<?php
$total_posts = wp_count_posts('strategic_events');
$total_posts = $total_posts->publish;
$number_shown = $paged * 10 - 10;?>
<?php while ( $my_posts->have_posts() ) : $my_posts->the_post() ?>
<a href="<?php echo get_permalink(get_the_ID());?>">
<div class="single-strategicevent col xs-col-16 md-col-8">
<div class="col xs-col-8 xs-offset-4 md-col-4 md-offset-0 center">
<div class="image-element">
<img src="<?php the_field('strategic_event_image');?>" class="circle">
<div class="image-overlay circle"></div>
</div>
</div>
<div class="col xs-col-14 xs-offset-1 md-col-10 md-offset-1">
<h5 class="strategic-event-title"><?php the_title();?></h5>
<?php if(get_field('subtitle')) :?>
<p><?php the_field('subtitle');?></p>
<small><?php the_field('location_text');?></small>
<?php endif;?>
</div>
</div>
</a>
<?php $number_shown++;
if ( $number_shown == $total_posts ) {
echo '<style>.loadmore.strats {display:none;}</style>';
}
endwhile ?>
<?php endif;
wp_die();
}
I'm all up for simplifying this into a single function if possible, or if not, at least getting it to work independently. I'm guessing that it's only viewing 1 wp_nonce's and checking it against both AJAX calls, so the first one it reads is fine, the second is coming up with a 403?
The problem I'm finding is that I can only get one to work at a time. Whichever function is added first in the functions.php, that one works - the other one gets a 403 error for admin-ajax.php.
Yes, because both your PHP functions (or AJAX callbacks) there are hooked to the same AJAX action, which is load_posts_by_ajax
:
// #1 AJAX action = load_posts_by_ajax
add_action('wp_ajax_load_posts_by_ajax', 'load_smart_maps_by_ajax_callback');
add_action('wp_ajax_nopriv_load_posts_by_ajax', 'load_smart_maps_by_ajax_callback');
// #2 AJAX action = load_posts_by_ajax
add_action('wp_ajax_load_posts_by_ajax', 'load_strats_by_ajax_callback');
add_action('wp_ajax_nopriv_load_posts_by_ajax', 'load_strats_by_ajax_callback');
And check_ajax_referer()
by default exits the page with a 403
("forbidden") status, when for example the nonce is not specified or has already expired:
// #1 $_REQUEST['security2'] will be checked for the load_smartmaps_posts nonce action.
check_ajax_referer('load_smartmaps_posts', 'security2');
// #2 $_REQUEST['security'] will be checked for the load_strats_posts nonce action.
check_ajax_referer('load_strats_posts', 'security');
You can send both nonces (although nobody would do that), or you can use the same nonce and same $_REQUEST
key (e.g. security
), but you'd still get an unexpected results/response.
So if you want a "single function", you can use a secondary "action":
PHP part in functions.php
:
// #1 Load More Smart Maps
// No add_action( 'wp_ajax_...' ) calls here.
function load_smart_maps_by_ajax_callback() {
// No need to call check_ajax_referer()
...your code here...
}
// #2 Load More Strategic Events
// No add_action( 'wp_ajax_...' ) calls here.
function load_strats_by_ajax_callback() {
// No need to call check_ajax_referer()
...your code here...
}
add_action( 'wp_ajax_load_posts_by_ajax', 'load_posts_by_ajax' );
add_action( 'wp_ajax_nopriv_load_posts_by_ajax', 'load_posts_by_ajax' );
function load_posts_by_ajax() {
check_ajax_referer( 'load_posts_by_ajax', 'security' );
switch ( filter_input( INPUT_POST, 'action2' ) ) {
case 'load_smartmaps_posts':
load_smart_maps_by_ajax_callback();
break;
case 'load_strats_posts':
load_strats_by_ajax_callback();
break;
}
wp_die();
}
JS part — the data
object:
// #1 On click of `.loadmore.smarties`
var data = {
'action': 'load_posts_by_ajax',
'action2': 'load_smartmaps_posts',
'security': '<?php echo wp_create_nonce( "load_posts_by_ajax" ); ?>', // same nonce
...other properties...
};
// #2 On click of `.loadmore.strats`
var data = {
'action': 'load_posts_by_ajax',
'action2': 'load_strats_posts',
'security': '<?php echo wp_create_nonce( "load_posts_by_ajax" ); ?>', // same nonce
...other properties...
};
But why not hook the callbacks to their own specific AJAX action:
// #1 AJAX action = load_smart_maps_posts_by_ajax
add_action('wp_ajax_load_smart_maps_posts_by_ajax', 'load_smart_maps_by_ajax_callback');
add_action('wp_ajax_nopriv_load_smart_maps_posts_by_ajax', 'load_smart_maps_by_ajax_callback');
// #2 AJAX action = load_strats_posts_by_ajax
add_action('wp_ajax_load_strats_posts_by_ajax', 'load_strats_by_ajax_callback');
add_action('wp_ajax_nopriv_load_strats_posts_by_ajax', 'load_strats_by_ajax_callback');
WP ajax is a bit strange. You enqueue the script to get it to show up on the page. You also LOCALIZE your script in order to stick variables into the page the script will need, variables such as nonces, ajax addresses, or even other weird things you may find useful.
Register and localize the script like this:
wp_enqueue_script( 'name_of_function', get_stylesheet_directory_uri() . '/js/my_special_script.js', array( 'jquery' ), '1.0', true );
wp_localize_script( 'name_of_function',
'name_the_script_will_see',
array(
'ajax_url' => admin_url('admin-ajax.php'),
'ajax_nonce' => wp_create_nonce('your_nonce'),
));
And then you have to add the ajax action TWICE, once for public and again for admin pages.
add_action('wp_ajax_this_is_the_ajax_name', 'function_name_in_php' );
add_action('wp_ajax_nopriv_this_is_the_ajax_name', 'function_name_in_php' );
Then in your script you pick up the values something like this:
var data = {
'action': 'this_is_the_ajax_name',
'post_nonce': name_the_script_will_see.ajax_nonce,
'other_value_needed': value_generated_by_script,
};
$.post( name_the_script_will_see.ajax_url, data, function( response_data ) {
if ( response_data.success ) {
alert ("Success");
} else {
alert ("Error");
}
Your php script will get the data as post data:
function function_name_in_php(){
$nonce = $_POST['post_nonce'];
$other_data = $_POST['other_value_needed'];
if ( ! wp_verify_nonce( $nonce, 'your_nonce' ) ) {
wp_send_json_error(array(
'message'=>'Security Token Failure',
)); // sends json_encoded success=false
}
I will use one nonce in many different scripts. All I'm really doing is making sure my requester is actually coming from my site. There's no point in generating 17 different nonces for that. Often I stick the nonce into a submission button as a data-attribute, then pull the nonce from that rather than from the wordpress localization scheme. It's more convenient when there are many scripts on a single page and they are accessing some of the same back end functions.
var postData = {};
var nonce = $('#register_button').attr('data-nonce');
postData.nonce = nonce;
postData.action = 'this_is_the_ajax_name';
$.post( name_the_script_will_see.ajax_url, postData, function( response_data ) {
if ( response_data.success ) {
alert ("success");
} else {
alert ("Error");
}
Hope this helps!
/wp-json/wp/v2/posts
to both render the initial posts, and pull in additional pages. Nonces etc shouldn't be necessary – Tom J Nowell ♦ Commented Dec 13, 2018 at 17:21