I am trying to create a live search using ajax and jquery on my custom WordPress theme. If i use .click() function it is showing desired output but when using .keyup or on('input',function(){}) it display nothing.
Jquery code
var searchRequest = null;
var minlength = 3;
$(".search-submit").on('input',function(e){
e.preventDefault();
var st=$(".search-field").val();
if (st.length >= minlength )
{
if (searchRequest != null)
searchRequest.abort();
searchRequest=$.ajax({
type:"POST",
url: "./wp-admin/admin-ajax.php",
data: {
action:'wpa56343_search',
search_string:st
},
success:function(data){
$('.srch-result').append(data);
}
});
}
});
PHP code of function.php
add_action('wp_ajax_nopriv_wpa56343_search', 'wpa56343_search');
add_action('wp_ajax_wpa56343_search', 'wpa56343_search');
function wpa56343_search()
{
global $wp_query;
$search = $_POST['search_string'];
$args = array(
's' => $search,
'posts_per_page' => 5
);
$query = new WP_Query( $args );
if ( $query->have_posts() ) :
while ( $query->have_posts() ) :
$query->the_post();
get_template_part( 'template-parts/ajax-search-format', get_post_format() );
endwhile;
endif;
wp_reset_postdata();
}
Please show your html structure too.
I think you are trying to trigger with the wrong element.
Maybe $(".search-submit") is not a input field.
Try:
$(".search-field").on('input',function(e){
//** YOUR CODE **/
});
Related
I'm trying to build out a Load More button in my Wordpress app, but the AJAX return value is always 'success: false', even though I know there are more posts in the DB. I followed a tutorial I found on YouTube, but not sure what I'm missing.
JS
jQuery(document).ready(function () {
const button = document.getElementById('loadmore');
button.addEventListener('click', () => {
let current_page = document.querySelector('#main-content').dataset.page;
// let max_pages = document.querySelector('#main-content').dataset.max;
const params = {
'action': 'load_more_posts',
'current_page': current_page
}
$.post('/wp-admin/admin-ajax.php', params, (data) => {
console.log(data);
});
});
})
PHP (functions.php)
wp_enqueue_script('loadmore', get_template_directory_uri() . '/js/loadmore.js', array('jquery'), filemtime(get_template_directory() . '/js/loadmore.js'));
add_action('wp_ajax_nopriv_load_more_posts', 'load_more_posts');
add_action('wp_ajax_load_more_posts', 'load_more_posts');
function load_more_posts() {
$next_page = $_POST['current_page'] + 1;
$query = new WP_Query([
'posts_per_page' => 12,
'paged' => $next_page
]);
if ($query->has_posts()):
ob_start();
while($query->have_posts()) : $query->the_post();
get_template_parts('partials/blog','posts');
endwhile;
wp_send_json_success(ob_get_clean());
else:
wp_send_json_error('no more posts');
endif;
}
The returned result continues to be:
data: "no more posts"
success: false
One thing that usually gets me too, your ajax function should always end in a wp_die(); function call, otherwise it will always return 0, even if the rest of your code is correct.
In this instance:
function load_more_posts() {
$next_page = $_POST['current_page'] + 1;
$query = new WP_Query([
'posts_per_page' => 12,
'paged' => $next_page
]);
if ($query->have_posts()):
ob_start();
while($query->have_posts()) : $query->the_post();
get_template_parts('partials/blog','posts');
endwhile;
wp_send_json_success(ob_get_clean());
else:
wp_send_json_error('no more posts');
endif;
wp_die();
}
Looks like u are passing wrong current page number, what there is in the let current_page = document.querySelector('#main-content').dataset.page; ??
My AJAX load more function doesn't check end of posts properly - it shows preloader one more time if there are no posts. This is caused by removing pleroader's hidden class every time before send ajax request.
How should I check end of posts correct?
AJAX
If response is 0 - just hide the preloader. So preloader is shown one more time when it shouldn't be.
var page = 3;
var emptyQuery = false;
function load_posts() {
page++;
var category = $('.cat-list__item.active').data('category');
$.ajax({
url: flow.ajax_url,
type: 'POST',
beforeSend: function (jqXHR) {
$('body').addClass('loading');
if ($('.preloader').length) {
$('.preloader').removeClass('preloader--hidden');
}
},
data: {
action: 'scroll_post_load',
page: page,
category: category
},
success: function (response) {
if ( response==0 ) {
emptyQuery = true;
if ($('.preloader').length) {
$('.preloader').addClass('preloader--hidden');
}
} else {
if ($('.preloader').length) {
$('.preloader').addClass('preloader--hidden');
}
var html = response;
$(html).hide().appendTo('.posts-list').fadeIn(1000);
}
$('body').removeClass('loading');
}
});
}
// Scripts which runs on scrolling
$( window ).on( 'scroll', function() {
if( $(window).scrollTop() > $('.posts-list').height() && !$('body').hasClass('loading')) {
if(emptyQuery == false) {
load_posts();
}
}
} );
PHP
If it is the last page - returns 0 in response
function scroll_post_load(){
$paged = $_POST['page'];
$category = $_POST['category'];
if( $category=='all' || !$category ) {
$args = array(
'post_type' => 'post',
'post_status' => 'publish',
'posts_per_page' => '3',
'paged' => $paged
);
}
if( $category ) {
$args = array(
'post_type' => 'post',
'post_status' => 'publish',
'posts_per_page' => '3',
'paged' => $paged,
'cat' => $category
);
}
$wp_query = new WP_Query( $args );
if ( $wp_query->max_num_pages == get_query_var('paged')) {
return '0';
}
if ($wp_query->have_posts()) :
while ($wp_query->have_posts()) : $wp_query->the_post();
show_template('loop-post');
endwhile;
endif;
wp_die();
}
add_action('wp_ajax_scroll_post_load', 'scroll_post_load');
add_action('wp_ajax_nopriv_scroll_post_load', 'scroll_post_load');
You can resolve this if you just return in your response what is the total number of pages and then in the script check if next page is last or something.
That's how I do it usually. I always return the max number of pages along with the content so I can check if I need to execute the AJAX call one last time.
I've created a Custom Post Type in Wordpress called Location/Tour and another one called Itinerary. In my CPT Itinerary, I have some ACF custom fields one of them is a repeater field that has subfields (Relationship field for the CPT Location/Tour, Title field, Description field).
I've created a button that should trigger an AJAX script which job is to get the values from the CPT Location/Tour(Title and Description) and
put them in my input subfields(Title and Description) in my CPT Itinerary.
I've created a PHP function that gets the values from the CPT Location/Tour and now I'm trying to run the PHP function using AJAX.
I was able to get the AJAX working and I get the values in my console log under ResponseText.
Now the part I'm struggling with. I need to set each value as a separate variable in JS so that I can replace the input field values with the new ones but unfortunately I don't know how.
I've tried almost everything and I think that I'm close to the answer but I'm missing something. :(
Here is my post-value-loader.php
<?php
// LOAD DEFAULT VALUES FROM DEFAULT TOUR
add_action('wp_ajax_post_loader', 'post_loader');
function post_loader($field) {
$post_id = $_POST["post_id"];
$args = array(
'p' => $post_id,
'numberposts'=> -1, // Fetch all posts...
'post_type'=> 'location_tour', // from the 'location_tour' CPT...
);
$location = new WP_Query( $args );
if ( $location->have_posts() ) : while ( $location->have_posts() ) : $location->the_post();
$title = the_field('title'); //The Title field value that we need
$description = the_field('description'); //The Description field value that we need
wp_reset_postdata();
?>
<?php endwhile; endif; ?>
<?php add_action('acf/prepare_field/name=default_tour', 'post_loader'); ?>
<?php }
// BUTTON TO RUN AJAX
function my_acf_prepare_field($field) {
echo '<div class="acf-field"><button type="submit" id="data_fetch" class="button acf-load-default-tour-values">Load default value</button></div>';
return $field;
}
add_action('acf/prepare_field/name=default_tour', 'my_acf_prepare_field');
// ADD SCRIPT TO WORDPRESS ADMIN AJAX
function js_data_fetch() {
wp_enqueue_script ("ajax-data-fetch", get_stylesheet_directory_uri() . "/inc/assets/js/data-fetch.js", array('jquery'));
//the_ajax_script will use to print admin-ajaxurl in data-fetch.js
wp_localize_script('ajax-data-fetch', 'the_ajax_script', array('ajaxurl' =>admin_url('admin-ajax.php')));
}
add_action("admin_enqueue_scripts", "js_data_fetch");
?>
And here is my data-fetch.js (Note: I'm not a JS guy :( )
jQuery(document).on( 'click', '#data_fetch', function( dohvati ){
dohvati.preventDefault();
var post_id = jQuery('.acf-row .selection .values ul li span').data('id'); // This takes the post ID from the selected Post(Location/Tour) in the Relationship field
jQuery.ajax({
url: the_ajax_script.ajaxurl, //The URL that we set for the wordpress admin-ajax.php
type: "POST",
dataType: 'json',
data: {
action: 'post_loader', // This is the name of the php function
post_id: post_id,
},
success: function(data){
console.log(data)
},
error: function(error){
console.log(error)
},
});
jQuery("#acf-field_5cb991a9337db-row-0-field_5cbeabc041c8a").val(title); //This is replacing the title field - but the variables are missing
jQuery("#acf-field_5cb991a9337db-row-0-field_5cbeab8f41c89").val(description); //This is replacing the description field - but the variables are missing
});
Also here are two images from the CPT Itinerary editor (https://imgur.com/kFImdpe) with the fields and my console log (https://imgur.com/wwxKXQP). Hope that this helps.
You have to return the data as JSON from post_loader function. I've cleaned up a little, but still, it's a mess.
// LOAD DEFAULT VALUES FROM DEFAULT TOUR
add_action('wp_ajax_post_loader', 'post_loader');
function post_loader() {
$post_id = $_POST["post_id"];
$args = array(
'p' => $post_id,
'numberposts'=> -1, // Fetch all posts...
'post_type'=> 'location_tour', // from the 'location_tour' CPT...
);
$location = new WP_Query( $args );
if ( $location->have_posts() ) :
while ( $location->have_posts() ) :
$location->the_post();
$title = the_field('title');
$description = the_field('description');
// You have to return data as json
wp_send_json([
'title' => $title,
'description' => $description
]);
//wp_reset_postdata();
endwhile;
endif;
// Why do you need this inside this function?
// add_action('acf/prepare_field/name=default_tour', 'post_loader');
}
JS
jQuery(document).on( 'click', '#data_fetch', function( dohvati ){
dohvati.preventDefault();
var post_id = jQuery('.acf-row .selection .values ul li span').data('id'); // This takes the post ID from the selected Post(Location/Tour) in the Relationship field
jQuery.ajax({
url: the_ajax_script.ajaxurl, //The URL that we set for the wordpress admin-ajax.php
type: "POST",
dataType: 'json',
data: {
action: 'post_loader', // This is the name of the php function
post_id: post_id,
},
success: function(data){
console.log(data)
jQuery("#acf-field_5cb991a9337db-row-0-field_5cbeabc041c8a").val(data.title); //This is replacing the title field - but the variables are missing
jQuery("#acf-field_5cb991a9337db-row-0-field_5cbeab8f41c89").val(data.description); //This is replacing the description field - but the variables are missing
},
error: function(error){
console.log(error)
},
});
});
I am using AJAX to load more products on a WooCommerce archive. I have used AJAX to "Load More" once before on this page. I have used the same code for the most part, just altered the WP_Query arguments to suit my needs. I can't understand why my code doesn't work.
JS
/**
* AJAX Load (Lazy Load) events
*/
$('#load-more').click( function(e){
e.preventDefault();
ajax_next_posts()
$('body').addClass('ajaxLoading');
});
var ajaxLock = false; // ajaxLock is just a flag to prevent double clicks and spamming
if( !ajaxLock ) {
function ajax_next_posts() {
ajaxLock = true;
// How many posts there's total
var totalPosts = parseInt( jQuery( '#found-posts' ).text() );
// How many have been loaded
var postOffset = jQuery( 'li.product' ).length
// How many do you want to load in single patch
var postsPerPage = 1;
// Ajax call itself
$.ajax({
method: 'POST',
url: leafshop.ajax_url,
data: {
action: 'ajax_next_posts',
post_offset: postOffset,
posts_per_page: postsPerPage,
product_cat: cat_id
},
dataType: 'json'
})
.done( function( response ) { // Ajax call is successful
// console.log( response );
// Add new posts
jQuery( '.product-grid' ).append( response[0] );
// Log SQL query
jQuery( '#query > pre' ).text( response[2] );
// Update the count of total posts
// jQuery( '#found-posts' ).text( response[1] );
ajaxLock = false;
console.log( 'Success' );
$('body').removeClass('ajaxLoading');
// How many posts there's total
var totalPosts = parseInt( jQuery( '#found-posts' ).text() );
console.log( "Total Posts: " + totalPosts );
// How many have been loaded
var postOffset = jQuery( 'li.product' ).length
console.log( "Posts currently showing: " + postOffset );
// Hide button if all posts are loaded
if( totalPosts < postOffset + ( 1 * postsPerPage ) ) {
jQuery( '#load-more' ).fadeOut();
}
})
// .fail( function() {
.fail( function(jqXHR, textStatus, errorThrown) { // Ajax call is not successful, still remove lock in order to try again
ajaxLock = false;
console.log(XMLHttpRequest);
console.log(textStatus);
console.log(errorThrown);
console.log( 'Failed' );
});
}
}
PHP
<?php
/**
* Load next 12 products using AJAX
*/
function ajax_next_posts() {
global $wpdb;
// Build query
$args = array(
'post_type' => 'product'
);
// Get offset
if( !empty( $_POST['post_offset'] ) ) {
$offset = $_POST['post_offset'];
$args['offset'] = $offset;
// Also have to set posts_per_page, otherwise offset is ignored
$args['posts_per_page'] = 12;
}
// Get posts per page
if( !empty( $_POST['posts_per_page'] ) ) {
// Also have to set posts_per_page, otherwise offset is ignored
$args['posts_per_page'] = $_POST['posts_per_page'];
}
// Set tax query if on cat page
if( !empty( $_POST['product_cat'] ) ){
$args['tax_query'] = array(
'taxonomy' => 'product_cat',
'terms' => array( (int)$_POST['product_cat'] ),
'field' => 'id',
'operator' => 'IN',
'include_children' => 1
);
}
$count_results = '0';
$ajax_query = new WP_Query( $args );
// Results found
if ( $ajax_query->have_posts() ) {
$count_results = $ajax_query->found_posts;
// Start "saving" results' HTML
$results_html = '';
ob_start();
while( $ajax_query->have_posts() ) {
$ajax_query->the_post();
echo wc_get_template_part( 'content', 'product' );
}
// "Save" results' HTML as variable
$results_html = ob_get_clean();
}
// Build ajax response
$response = array();
// 1. value is HTML of new posts and 2. is total count of posts
global $wpdb;
array_push ( $response, $results_html, $count_results, $wpdb->last_query );
echo json_encode( $response );
// Always use die() in the end of ajax functions
die();
}
add_action('wp_ajax_ajax_next_posts', 'ajax_next_posts');
add_action('wp_ajax_nopriv_ajax_next_posts', 'ajax_next_posts');
The AJAX call runs successfully but doesn't return what I'd expect it to. I'd expect it to return the next product from the current category, instead it returns a product from a different category. The development site is accessible on http://leaf.kalodigital.co.uk and I have been using the "Black Tea" page in my testing. Any help would be greatly appreciated.
Method
After some further research, I found that WooCommerce contains a class names WC_Poduct_Query. Following their documentation, found here, I rebuilt my query. This was working in the sense that it was querying for the correct products and returns the ones I'd expect it to as an array of WC_Product_Variable Objects. I proceeded to, within a foreach loop, use setup_postdata( $post ); to set the product Objects up so that I could use the wc_get_template_parts(); function to call the content-product.php template to format the output of the data. I found that this didn't work, although I couldn't figure to why this was the case, using setup-postdata(); was causing the object to turn into [{id:0,filter:raw},{id:0,filter:raw}]. I imagine this would be related to the WC_Product_Variable Objects not matching the expect format that setup_postdata(); usually expects from a WP_Post Object.
Nonetheless, I reverted to using WP_Query for my query, rebuilt the query script from scratch and, would you believe it, everything worked as expected to. Below is my working code to AJAX load the next "bunch" of products on any WooCommerce Archive page, at the click of a button.
Code
JS
/**
* AJAX Load (Lazy Load) events
*/
//-- Settings
// How many do you want to load each button click?
var postsPerPage = 12;
//-- /Settings
// How many posts there's total
var totalPosts = parseInt( jQuery( '#found-posts' ).text() );
// if( totalPosts == postOffset ) {
// jQuery( '#load-more' ).fadeOut();
// }
$('#load-more').click( function(e){
e.preventDefault();
// Get current category
var cat_id = $(this).data('product-category');
ajax_next_posts( cat_id );
$('body').addClass('ajaxLoading');
});
var ajaxLock = false; // ajaxLock is just a flag to prevent double clicks and spamming
if( !ajaxLock ) {
function ajax_next_posts( cat_id ) {
ajaxLock = true;
// How many have been loaded
var postOffset = jQuery( 'li.product' ).length;
// Ajax call itself
$.ajax({
method: 'POST',
url: leafshop.ajax_url,
data: {
action: 'ajax_next_posts',
post_offset: postOffset,
posts_per_page: postsPerPage,
product_cat: cat_id
},
dataType: 'json'
})
.done( function( response ) { // Ajax call is successful
// Add new posts
jQuery( '.product-grid' ).append( response[0] );
// Update Post Offset
var postOffset = jQuery( 'li.product' ).length;
ajaxLock = false;
console.log( 'Success' );
$('body').removeClass('ajaxLoading');
// How many posts there's total
console.log( "Total Posts: " + totalPosts );
// How many have been loaded
var postOffset = jQuery( 'li.product' ).length
console.log( "Posts on Page: " + postOffset );
// Hide button if all posts are loaded
if( ( totalPosts - postOffset ) <= 0 ) {
jQuery( '#load-more' ).fadeOut();
}
})
// .fail( function() {
.fail( function(jqXHR, textStatus, errorThrown) { // Ajax call is not successful, still remove lock in order to try again
ajaxLock = false;
console.log(XMLHttpRequest);
console.log(textStatus);
console.log(errorThrown);
console.log( 'Failed' );
});
}
}
PHP
<?php
/**
* Load next 12 products using AJAX
*/
function ajax_next_posts() {
global $product;
// Build Query
$args = array(
'post_type' => 'product',
'posts_per_page' => (int)$_POST['posts_per_page'],
'orderby' => 'title',
'order' => 'ASC',
'offset' => (int)$_POST['post_offset'],
);
if( !empty( $_POST['product_cat'] ) ) {
$args['tax_query'] = array(
'relation' => 'AND',
array (
'taxonomy' => 'product_cat',
'field' => 'slug',
'terms' => $_POST['product_cat'],
'operator' => 'IN'
),
);
}
$count_results = '0';
$ajax_query = new WP_Query( $args );
// Results found
if( $ajax_query->have_posts() ){
// Start "saving" results' HTML
$results_html = '';
ob_start();
while( $ajax_query->have_posts() ) {
$ajax_query->the_post();
echo wc_get_template_part( 'content', 'product' );
}
wp_reset_postdata();
// "Save" results' HTML as variable
$results_html = ob_get_clean();
} else {
// Start "saving" results' HTML
$results_html = '';
ob_start();
echo "none found!";
// "Save" results' HTML as variable
$results_html = ob_get_clean();
}
// Build ajax response
$response = array();
// 1. value is HTML of new posts and 2. is total count of posts
array_push ( $response, $results_html );
echo json_encode( $response );
// Always use die() in the end of ajax functions
die();
}
add_action('wp_ajax_ajax_next_posts', 'ajax_next_posts');
add_action('wp_ajax_nopriv_ajax_next_posts', 'ajax_next_posts');
Use Cases
By refactoring the supplied AJAX code, it would be possible to turn this solution into an "infinite scrolling" system as opposed to a "Load More" button solution. Please feel free to reuse this code where it may see fit!
I'm trying to load post with ajax using a variable to get posts by year. I've found this great tutorial https://www.creare.co.uk/blog/simple-wp_query-ajax and I'm trying to modify it to my needs but I have 0 experience with Ajax and not so much with jquery...
this is my modified code but the year is not passed and I don't know how to do it, I appreciate any help
js
//If input is changed, load posts
$('#genre-filter input').live('change', function(){
genre_get_posts(); //Load Posts
});
//Find Selected Genres
function getSelectedGenres()
{
var genres = [];
$("#genre-filter div").click(function() {
var genres = $(this).attr('value');
console.log(genres);
});
return genres;
}
//Main ajax function
function genre_get_posts(paged)
{
var ajax_url = ajax_genre_params.ajax_url;
$.ajax({
type: 'GET',
url: ajax_url,
data: {
action: 'genre_filter',
genres: getSelectedGenres,
},
beforeSend: function ()
{
//Show loader here
},
success: function(data)
{
//Hide loader here
$('#genre-results').html(data);
},
error: function()
{
$("#genre-results").html('<p>There has been an error</p>');
}
});
}
template
<section id="primary" class="content-area">
<div id="content" class="site-content" role="main">
<?php
if ( have_posts() ):
while ( have_posts() ): the_post();
get_template_part( 'content' );
endwhile;
endif;
?>
<div class="entry-content">
<div id="genre-filter">
<div id="2018" value="2018" name="filter_genre[]">2018</div>
<div id="2017" value="2017" name="filter_genre[]">2017</div>
</div>
<div id="genre-results"></div>
</div>
</div>
functions.php
function ajax_genre_filter() {
$query_data = $_GET;
$genre_terms = ( $query_data[ 'genres' ] ) ? explode( ',', $query_data[ 'genres' ] ) : false;
$book_args = array(
'post_type' => 'book',
'posts_per_page' => 2,
'year' => $genre_terms,
);
$book_loop = new WP_Query( $book_args );
if ( $book_loop->have_posts() ):
while ( $book_loop->have_posts() ): $book_loop->the_post();
get_template_part( 'content' );
endwhile;
echo $query_data;
else :
get_template_part( 'content-none' );
endif;
wp_reset_postdata();
die();
}
Your first issue is variable scoping in getSelectedGenres. I would imaging you're always getting an empty array due to the 2nd var in your event handler. Try the modified version below.
function getSelectedGenres()
{
var genres = [];
$("#genre-filter div").click(function() {
var g = $(this).attr('value');
genres.push(g);
console.log(g);
});
return genres;
}
Additionally, it looks like ajax_genre_filter reads the year from the genres query string genres based on the mapping 'year' => $genre_terms so try including the selected year in your function getSelectedGenres.