Ask your WordPress questions! Pay money and get answers fast! Comodo Trusted Site Seal
Official PayPal Seal

I want to "load more" posts with Ajax in a custom loop. Everything wor WordPress

  • SOLVED

I want to "load more" posts with Ajax in a custom loop. Everything works so far - the "loadmore" button hides when there are no more posts to load.

BUT it would be nice when the loadmore button hides automatically when there are no more posts to load so the user doesn't need to click once more to get the message "No More Records Found". I want this message to be shown without extra last click.

The loop in my template
<?php
$date_now = date('Y-m-d H:i:s');
$args = array(
'posts_per_page' => 2,
'post_type' => 'termin',
'meta_query' => array(
'key' => 'end_date',
'compare' => '>=',
'value' => $date_now,
'type' => 'DATETIME'
),
'orderby' => 'end_date',
'order' => 'ASC',
'meta_type' => 'DATE',
'post_status' => 'publish',
'paged' => $paged,
);
$my_posts = new WP_Query( $args );
if ( $my_posts->have_posts() ) :
?>
<div class="ajax-posts">
<?php while ( $my_posts->have_posts() ) : $my_posts->the_post(); ?>
<article><?php the_title(); ?></article>
<?php endwhile; ?>
</div>
<?php endif; ?>
<button class="loadmore">Mehr laden</button>
<span class="loading">lade ...</span>


in my functions.php
add_action('wp_ajax_load_posts_by_ajax', 'load_posts_by_ajax_callback');
add_action('wp_ajax_nopriv_load_posts_by_ajax', 'load_posts_by_ajax_callback');

function load_posts_by_ajax_callback() {
check_ajax_referer('load_more_posts', 'security');
$date_now = date('Y-m-d H:i:s');
$paged = $_POST['page'];
$args = array(
'posts_per_page' => 2,
'post_type' => 'termin',
'meta_query' => array(
'key' => 'end_date',
'compare' => '>=',
'value' => $date_now,
'type' => 'DATETIME'
),
'orderby' => 'end_date',
'order' => 'ASC',
'meta_type' => 'DATE',
'post_status' => 'publish',
'paged' => $paged,
);
$my_posts = new WP_Query( $args );
if ( $my_posts->have_posts() ) :
?>
<?php while ( $my_posts->have_posts() ) : $my_posts->the_post(); ?>
<article><?php the_title(); ?></article>
<?php endwhile; ?>
<?php
endif;

wp_die();
}


Finally the jQuery script (in footer.php)
<script type="text/javascript">
var ajaxurl = "<?php echo admin_url( 'admin-ajax.php' ); ?>";
var page = 2;
$('.loading').hide();
jQuery(function($) {
$('body').on('click', '.loadmore', function(e) {

$('.loadmore').hide();
var data = {
'action': 'load_posts_by_ajax',
'page': page,
'security': '<?php echo wp_create_nonce("load_more_posts"); ?>'
};

$.post(ajaxurl, data, function(response) {
if(response) {
$('.ajax-posts').append(response);
$('.loadmore').show();
$('.loading').hide();
page++;
} else {
$(".ajax-posts").append("No More Records Found");
$('.loading').hide();
$('.loadmore').hide();
}
});
});
});
</script>

Answers (6)

2019-08-30

Cesar Contreras answers:

Do you have a test site that I can see online?

2019-08-30

John Cotton answers:

If you change the way you return content on the ajax call, you can pass an additional value to test whether any more pages are left.

So, instead of echoing the title in the loop, assign it to a variable:

$html .= sprintf( '<article>%s</article>', get_the_title() );


Then, get the number of pages found and test that against your current page:

global $wp_query;
$last_page = $wp_query->max_num_pages > $paged;


Next, return both of these values via ajax:

wp_send_json( array( 'html' => $html, 'last_page' => $last_page ) );


Finally, change your js to make use of this new structure:

if(response) {
$('.ajax-posts').append(response.data.html);
if( !response.data.last_page ) $('.loadmore').show();
$('.loading').hide();
page++;
}


Hopefully, those snippets above will solve it for you.


herrfischer comments:

jQuery code is clear but I don't know where to put all that PHP code. Btw. I don't have just the title in the query, there are a lot of custom fields and such things:

<article>
<a href="<?php the_permalink(); ?>"></a>

<footer class="aktuelles_stream_date">
<?php
$date = get_field('end_date', false, false);
$date = new DateTime($date);
?>

<span><?php echo $date->format('d'); ?></span>
<span><?php echo $date->format('m'); ?></span>
<br clear="left" />
<span><?php echo $date->format('Y'); ?></span>
</footer>

<?php if ( get_field('bild_1') ) : ?>
<?php
$image = get_field( 'bild_1' );
$size = 'full';
$img = wp_get_attachment_image_src( $image, $size );
?>
<header class="termin_postthumb" style="background-image: url('<?php echo $img[0] ?>');"></header>
<?php endif; ?>

<div class="article_content">
<h3 class="h4">
<?php the_title(); ?>
</h3>

<?php
$schulen = get_field('schulen');
if( $schulen ):
?>
<ul>
<?php foreach( $schulen as $schule ): ?>
<li>
<h4 class="termine_stream_relation_schule">
<?php echo $schule; ?>
</h4>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>

<?php
$post_object = get_field('termin_locations');
if( $post_object ):
$post = $post_object;
setup_postdata( $post );
?>
<h4 class="termine_stream_relation_location">
<?php echo get_the_title($post_object); ?>
</h4>
<?php endif; ?>

</div>
</article>


John Cotton comments:

What I am suggesting is that, instead of writing the HTML out to the browser as you, you store it in a string to write out as part of the JSON response.

So, taking your code above, you would rewrite it along these lines:


$p = get_the_permalink();
$date = get_field('end_date', false, false);
$date = new DateTime($date);

$html .= <<< EOT
<article>
<a href="$p"></a>
<footer class="aktuelles_stream_date">
<span>{$date->format('d')}</span>
<span>{$date->format('m')}</span>
<br clear="left" />
<span>{$date->format('Y')}</span>
</footer>
<!-- etc etc... -->
EOT;

2019-08-30

Andrea P answers:

I just had a quick look but John Cotton suggestions seems good to me too.

the only thing is that I am not sure about the way the $last_page is calculated.
Considering that it is a custom loop, it shouldn't use the global $wp_query but instead be something like this:

$last_page = $my_posts->max_num_pages > $paged;


John Cotton comments:

Yep, that will work too :)

2019-08-31

Arnav Joy answers:

Hello,
Here is one simplest solution, you can add following like to functions.php just before wp_die();
if( $my_posts->max_num_pages == $paged )
echo '<style>.loadmore{ display:none !important; }</style>';



so final code for the functions.php file will be this

add_action('wp_ajax_load_posts_by_ajax', 'load_posts_by_ajax_callback');
add_action('wp_ajax_nopriv_load_posts_by_ajax', 'load_posts_by_ajax_callback');

function load_posts_by_ajax_callback() {
check_ajax_referer('load_more_posts', 'security');
$date_now = date('Y-m-d H:i:s');
$paged = $_POST['page'];
$args = array(
'posts_per_page' => 2,
'post_type' => 'termin',
'meta_query' => array(
'key' => 'end_date',
'compare' => '>=',
'value' => $date_now,
'type' => 'DATETIME'
),
'orderby' => 'end_date',
'order' => 'ASC',
'meta_type' => 'DATE',
'post_status' => 'publish',
'paged' => $paged,
);
$my_posts = new WP_Query( $args );
if ( $my_posts->have_posts() ) :
?>
<?php while ( $my_posts->have_posts() ) : $my_posts->the_post(); ?>
<article><?php the_title(); ?></article>
<?php endwhile; ?>
<?php
endif;
if( $my_posts->max_num_pages == $paged )
echo '<style>.loadmore{ display:none !important; }</style>';

wp_die();
}



Let me know if that works.


herrfischer comments:

Simple and short and it is okay for me - I do not have time to put more effort in this - thanks!.

I tried to work with "$my_posts->max_num_pages" in the jQuery script but it didn't worked.


Arnav Joy comments:

have you tried my code above?
this will work with your original code and changes are there in only functions.php file as I told you above.
if you don't have time to implement this yourself, I can help you.
if ok please send me mail at: [email protected]


Arnav Joy comments:

so again you just have to use this code in functions.php file above wp_die();
if( $my_posts->max_num_pages == $paged )
echo '<style>.loadmore{ display:none !important; }</style>';


Arnav Joy comments:

so in place of wp_die();
you will have this

if( $my_posts->max_num_pages == $paged )
echo '<style>.loadmore{ display:none !important; }</style>';

wp_die();


Arnav Joy comments:

please check my reply above.

2019-08-31

Mohamed Ahmed answers:

I already did codes like that in web hosting site search by features
So, the load more button will be in a condition (replace your code with this)


<?php
$date_now = date('Y-m-d H:i:s');
$args = array(
'posts_per_page' => 2,
'post_type' => 'termin',
'meta_query' => array(
'key' => 'end_date',
'compare' => '>=',
'value' => $date_now,
'type' => 'DATETIME'
),
'orderby' => 'end_date',
'order' => 'ASC',
'meta_type' => 'DATE',
'post_status' => 'publish',
'paged' => $paged,
);
$my_posts = new WP_Query( $args );
if ( $my_posts->have_posts() ) :
?>
<div class="ajax-posts">
<?php while ( $my_posts->have_posts() ) : $my_posts->the_post(); ?>
<article><?php the_title(); ?></article>
<?php endwhile; ?>
</div>
<?php
if ((isset($_REQUEST['page']))) {
$this_page = $_REQUEST['page'];
} else {
$this_page = $my_posts->get('paged');
}

$max_page = $my_posts->max_num_pages;
if ($paged <= ($max_page - 1)) {
?>
<button class="loadmore" data-url='" . admin_url('admin-ajax.php') . '?action=load_posts_by_ajax_callback&page=' . ($this_page + 1) . "'>Mehr laden</button>
<span class="loading">lade ...</span>
<?php } ?>
<?php endif; ?>


After your ajax function


jQuery('body').on('click', '.loadmore', function(e) {
e.preventDefault();
var Url = jQuery(this).attr('data-url');
jQuery('.loading').last().load(Url);
jQuery('.loading').last().html('<button class="loadmore">Mehr laden</button>');
jQuery('.loadmore').last().remove();

return false
})


I can send you my live example also
Regards


herrfischer comments:

I get "unrecognized expression: . admin_url(".


Mohamed Ahmed comments:

You will add my jQuery code (into your ajax code )
Your Ajax code will be


<script type="text/javascript">
var ajaxurl = "<?php echo admin_url( 'admin-ajax.php' ); ?>";
var page = 2;
$('.loading').hide();
jQuery(function($) {
$('body').on('click', '.loadmore', function(e) {

var Url = jQuery(this).attr('data-url');
jQuery('.loading').last().load(Url);
jQuery('.loading').last().html('<button class="loadmore">Mehr laden</button>');
jQuery(".loadmore").append("No More Records Found");
var data = {
'action': 'load_posts_by_ajax',
'page': page,
'security': '<?php echo wp_create_nonce("load_more_posts"); ?>'
};

$.post(ajaxurl, data, function(response) {
if(response) {
$('.ajax-posts').append(response);
}
});
});
});
</script>


herrfischer comments:

When I click on "Mehr laden" (load more) I still get "jquery.min.js?ver=5.2.2:2 Uncaught Error: Syntax error, unrecognized expression: . admin_url(" and he always loads the next to endlessly.

this:
<button class="loadmore" data-url='" . admin_url('admin-ajax.php') . '?action=load_posts_by_ajax_callback&page=' . ($this_page + 1) . "'>Mehr laden</button>

looks strange in my editor, like there is a syntax error.


Mohamed Ahmed comments:

It must be in a PHP tag

<button class="loadmore" data-url="<?php echo admin_url('admin-ajax.php') . "?action=load_posts_by_ajax_callback&page=" . $this_page + 1 . ""; ?>>Mehr laden</button>

You can email me to send you live example
[email protected]

2019-09-01

Mohammed Remeez answers:

Hello brother,
Can you try this simple logical solutions.
In your script.
1. Get total no. of pages for your query.
2. If (response),
check if total no. of pages === current page.
If yes, $('.loadmore').hide
Else, $('.loadmore').show
To get total no. of pages try this:
Var total_pages = <?php echo $my_posts->max_num_pages; ?>

Thus your final script will be like this,

<script type="text/javascript">
var ajaxurl = "<?php echo admin_url( 'admin-ajax.php' ); ?>";
var total_pages = "<?php echo $my_posts->max_num_pages; ?>" //modified part.
var page = 2;
$('.loading').hide();
jQuery(function($) {
$('body').on('click', '.loadmore', function(e) {

$('.loadmore').hide();
var data = {
'action': 'load_posts_by_ajax',
'page': page,
'security': '<?php echo wp_create_nonce("load_more_posts"); ?>'
};

$.post(ajaxurl, data, function(response) {
if(response) {
$('.ajax-posts').append(response);
$('.loading').hide();
/* modified part */
if( page == total_pages ){
$('.loadmore').hide();
$(".ajax-posts").append("No More Records Found");
}else{
$('.loadmore').show();
}
page++;
}
});
});
});
</script>

Thanks.


herrfischer comments:

I had the same idea before but I get "Undefined variable: my_posts"