WordPress / Web Development Tutorials
(Best WordPress Tutorials)

CSSHTMLJavaScriptjQueryMySQLPHPSilvaTechnologiesWooCommerceWordpress
Silva Web Designs - Blog

How to Load More Posts using Ajax with a Button or on Scroll in WordPress

Updated: 22/09/2021 – Further clarification has been added on the setup of this script.

In today’s post, we will show you how to create a load more button to show additional posts or custom post types using AJAX.

The benefit of this is that it will improve the page-load of the particular page as it will only be displaying a certain amount of posts before having to load any more of the content (especially when you are loading images).

So let’s get started…

So the first thing that you should have is a list of posts to display on the frontend as follows:-


    <div id="ajax-posts" class="row">
        <?php
            $postsPerPage = 3;
            $args = array(
                    'post_type' => 'post',
                    'posts_per_page' => $postsPerPage,
            );

            $loop = new WP_Query($args);

            while ($loop->have_posts()) : $loop->the_post();
        ?>

         <div class="small-12 large-4 columns">
                <h1><?php the_title(); ?></h1>
                <p><?php the_content(); ?></p>
         </div>

         <?php
                endwhile;
        wp_reset_postdata();
         ?>
    </div>
    <div id="more_posts">Load More</div>

Then you want to add the following code in your functions.php file where you register the scripts:


	wp_localize_script( 'core-js', 'ajax_posts', array(
	    'ajaxurl' => admin_url( 'admin-ajax.php' ),
	    'noposts' => __('No older posts found', 'twentyfifteen'),
	));	

To further explain this, in my example I enqueue scripts as follows:


wp_register_script( 'core-js', get_template_directory_uri() . '/assets/js/core.js');
wp_enqueue_script( 'core-js' );

wp_localize_script( 'core-js', 'ajax_posts', array(
    'ajaxurl' => admin_url( 'admin-ajax.php' ),
    'noposts' => __('No older posts found', 'twentyfifteen'),
));

Firstly, you would enqueue the file where you will be adding the JS code to, then you will add the wp_localize_script code after you have enqueued the core.js file. This has to be done like this otherwise you will get an error such as Can’t find variable: ajax_posts.


NOTE: You will need to replace code-js with the file you are going to be adding the JS code later in this tutorial.

We added it to a file called core.js, and the reason we added code-js is that we enqueued the script as follows:


	wp_register_script( 'core-js', get_template_directory_uri() . '/assets/js/core.js');
	wp_enqueue_script( 'core-js' );

Right after the existing wp_localize_script. This will load WordPress own admin-ajax.php so that we can use it when we call it in our ajax call.

At the end of the functions.php file, you need to add the function that will load your posts:-


function more_post_ajax(){

    $ppp = (isset($_POST["ppp"])) ? $_POST["ppp"] : 3;
    $page = (isset($_POST['pageNumber'])) ? $_POST['pageNumber'] : 0;

    header("Content-Type: text/html");

    $args = array(
        'suppress_filters' => true,
        'post_type' => 'post',
        'posts_per_page' => $ppp,
        'paged'    => $page,
    );

    $loop = new WP_Query($args);

    $out = '';

    if ($loop -> have_posts()) :  while ($loop -> have_posts()) : $loop -> the_post();
        $out .= '<div class="small-12 large-4 columns">
                <h1>'.get_the_title().'</h1>
                <p>'.get_the_content().'</p>
         </div>';

    endwhile;
    endif;
    wp_reset_postdata();
    die($out);
}

add_action('wp_ajax_nopriv_more_post_ajax', 'more_post_ajax');
add_action('wp_ajax_more_post_ajax', 'more_post_ajax');

The final part is the ajax itself. In core.js or your main JavaScript/jQuery file, you need to input inside the $(document).ready(); environment the following code:-


var ppp = 3; // Post per page
var pageNumber = 1;


function load_posts(){
    pageNumber++;
    var str = '&pageNumber=' + pageNumber + '&ppp=' + ppp + '&action=more_post_ajax';
    $.ajax({
        type: "POST",
        dataType: "html",
        url: ajax_posts.ajaxurl,
        data: str,
        success: function(data){
            var $data = $(data);
            if($data.length){
                $("#ajax-posts").append($data);
                //$("#more_posts").attr("disabled",false); // Uncomment this if you want to disable the button once all posts are loaded
                $("#more_posts").hide(); // This will hide the button once all posts have been loaded
            } else{
                $("#more_posts").attr("disabled",true);
            }
        },
        error : function(jqXHR, textStatus, errorThrown) {
            $loader.html(jqXHR + " :: " + textStatus + " :: " + errorThrown);
        }

    });
    return false;
}

$("#more_posts").on("click",function(){ // When btn is pressed.
    $("#more_posts").attr("disabled",true); // Disable the button, temp.
    load_posts();
    $(this).insertAfter('#ajax-posts'); // Move the 'Load More' button to the end of the the newly added posts.
});

With the above code, you should now have a load more button at the bottom of your posts, and once you click this it will display further posts. This can also be used with CTP (custom post type).

How to add Infinite Load, instead of Click

You can also load the rest of the posts automatically on scroll instead of having to click on a button. This can be achieved by making it invisible by setting the visibility to hidden.

The following code will run the load_posts() function when you’re 100px from the bottom of the page. So instead of the click function we added we would use:


$(window).on('scroll', function(){
    if($('body').scrollTop()+$(window).height() > $('footer').offset().top){
        if(!($loader.hasClass('post_loading_loader') || $loader.hasClass('post_no_more_posts'))){
                load_posts();
        }
    }
});

Do note that there is a drawback to this. With this method, you could never scroll the value of $(document).height() - 100 or $('footer').offset().top, If that should happen, just increase the number where the scroll goes to.

You can easily check it by putting console.log in your code and see in the inspector what they throw out like so:


$(window).on('scroll', function () {
    console.log($(window).scrollTop() + $(window).height());
    console.log($(document).height() - 100);
    if ($(window).scrollTop() + $(window).height()  >= $(document).height() - 100) {
        load_posts();        
    }
});

Adjust the settings accordingly and you will get this working to your desire in no time!

If you need any help with this, feel free to get in touch in the comment below. If you need help with this, just drop me a message and I’ll be more than happy to help you!

 


Sponsor

If you have come this far and you’re a fan of Football, check out our sponsor below; The FPL Way. We have just created an excellent tool for them; The FPL Planner.

They will help you become the king of your FPL mini-leagues!


Fantasy Premier League – Gain the FPL advantage you deserve

 

Nathan da Silva - Profile

Posted by: Nathan da Silva

Nathan is the Founder of Silva Web Designs. He is passionate about web development, website design and basically anything digital-related. His main expertise is with WordPress and various other CMS frameworks. If you need responsive design, SEO, speed optimisation or anything else in the world of digital, you can contact Silva Web Designs here; [email protected]

It’s good to share

57 thoughts on “How to Load More Posts using Ajax with a Button or on Scroll in WordPress

  1. Hey. I am getting this error
    ReferenceError: Can’t find variable: ajax_posts.

    Looks like this is only for twentyfifteen theme? I am trying to use it on my custom theme. I copied things from twentyfifteen, but it didn’t change anything about this error.

    1. The code will work with any theme, do you have a link that I can check? You shouldn’t be getting an error if the code has been added to your functions.php file.

  2. Edit: after copying functions.js and enqueuing it error disapeared but posts are still not loading. every click loads admin-ajax.php and that’s all.

    1. I have a different issue probably, even pagination didn’t show up. It is copied code for custom loop, so I don’t know why. I am working on it.

  3. Nooow I got it. I was using different post type and I didn’t realize I had to change it in the functions.php part too, damn, so much time wasted for this.

    1. Sorry one last reply 😀
      To be helpful too, I found a typo. There is one extra quote here / ”&pageNumber=’ /

      1. Haha, I realised there was a bit of confusion in setting this up. I’m glad you’ve resolved the issue, I’ve updated the post to clarify things and also corrected the issue with the additional quote ; ) Thanks for raising that for me, I’m sure it will help others.

  4. Would really appreciate some help with this, my current query has 24 posts per page and I use:
    $index = 1; $totalNum = $wp_query->found_posts;
    to put each set of 3 inside a div with class “row”.
    So on the news page I currently have:
    row
    post 1
    post 2
    post 3
    end of row
    row
    post 4
    post 5
    post 6
    end of row etc…

    How can I get this same effect when load more is clicked? I have tried to insert the same type of query and echo a div with class row but it just doesnt seem to add the new posts inside the row div.
    Thanks

    1. Hi Adrian,

      Can you further explain the issue that you are having, or do you have a development link where I can see the issue that you are facing? I’l be able to provide a solution once you send me this.

      Thanks,

      Nathan

    1. Ahhh okay, I can see the issue now.

      What you need to do is replace the more_post_ajax part in in your functions.php with the following:-

      
      function more_post_ajax(){
      
          $ppp = (isset($_POST["ppp"])) ? $_POST["ppp"] : 3;
          $page = (isset($_POST['pageNumber'])) ? $_POST['pageNumber'] : 0;
      
          header("Content-Type: text/html");
      
          $args = array(
              'suppress_filters' => true,
              'post_type' => 'post',
              'posts_per_page' => $ppp,
              'paged'    => $page,
          );
      
          $loop = new WP_Query($args);
      
          $out = '';
      
      	$out = '<div class="row">';
          if ($loop -> have_posts()) :  $i = 1; while ($loop -> have_posts()) : $loop -> the_post();    	
              $out .= '<div class="post">;
                      	<h1>'.get_the_title().'</h1>
                      	<p>'.get_the_content().'</p>
             			</div>';
             	if($i % 3 == 0) {$out .= '</div><div>';}
      
          $i++; endwhile;
          endif;
          wp_reset_postdata();
          die($out);
      }
      
      add_action('wp_ajax_nopriv_more_post_ajax', 'more_post_ajax');
      add_action('wp_ajax_more_post_ajax', 'more_post_ajax');
      

      The $out variable is what is being outputted in the HTML code when you click load more.

      In your case scenario, you are loading 3 items at a time, so I added the <div class="row"> outside of the loop and then echoing the closing div for this element every 3 elements, hope that makes sense.

      I think I will update this post with a more updated version, I think some more clarity needs to be made on this post.

      1. Hi Nathan

        Thank you for the help so far, almost there. If you take another look now you’ll see your code worked a treat, but there seems to be an extra empty row div at the end after the row of posts, if you click load more a few times theres a row with 3 posts then an empty div then another row then an empty one. The load more button is also appearing inside a row div where it’s not in the template (presumably for the same reason). And any way of the posts appearing above the load more button not below it?

        1. Okay, sorry my bad, please change the code to:

          
          function more_post_ajax(){
          
              $ppp = (isset($_POST["ppp"])) ? $_POST["ppp"] : 3;
              $page = (isset($_POST['pageNumber'])) ? $_POST['pageNumber'] : 0;
          
              header("Content-Type: text/html");
          
              $args = array(
                  'suppress_filters' => true,
                  'post_type' => 'post',
                  'posts_per_page' => $ppp,
                  'paged'    => $page,
              );
          
              $loop = new WP_Query($args);
          
              $out = '';
          
          	$out = '<div class="row">';
              if ($loop -> have_posts()) :  $i = 1; while ($loop -> have_posts()) : $loop -> the_post();    	
                  $out .= '<div class="post">
                          	<h1>'.get_the_title().'</h1>
                          	<p>'.get_the_content().'</p>
                 			</div>';
                 	if($i % 3 == 0) {$out .= '</div>';}
          
              $i++; endwhile;
              endif;
              wp_reset_postdata();
              die($out);
          }
          
          add_action('wp_ajax_nopriv_more_post_ajax', 'more_post_ajax');
          add_action('wp_ajax_more_post_ajax', 'more_post_ajax');
          

          Then, to append the ‘load more’ to always appear at the end of the posts, please update your JS for the click function to:

          
              $("#more_posts").on("click",function(){ // When btn is pressed.
                  $("#more_posts").attr("disabled",true); // Disable the button, temp.
                  load_posts();
                  $(this).insertAfter('#ajax-posts');
              });
          

          Should be all good now 😉

          1. Hi Nathan
            Thanks again for your further reply, I have finally got the ajax loading working, my total post count wasn’t returning the correct figure so when I used $i = $totalNum it wasn’t stopping the rows from being added hence the additional divs being created at the end.
            So the loading is all working great now, unfortunately the only thing not working at this point is the load more link being at the end, I added $(this).insertAfter(‘#ajax-posts’); as suggested but this didn’t work. Also tried $(this).appendTo($(‘#ajax-posts’));

          2. That’s odd, it’s working on my test site with $(this).insertAfter('#ajax-posts');

            You could try adding it here:

            
            if($data.length){
                $("#ajax-posts").append($data);
                $("#more_posts").attr("disabled",false);
                $(this).insertAfter('#ajax-posts'); // Add here
            }
            

            It actually seems to work on the second click though which is strange.

            What happens if you add a short delay, like:

            
            setTimeout(function(){
                $(this).insertAfter(‘#ajax-posts’);
            },500);
            

            It could be possible the element is being moved before the element has time to populate.

            Pretty sure one of the 2 suggestions above will work anyway. Let me know : )

    2. I have also updated the post now to clarify a few things on the setup of this, let me know if the code above fixes the issue you are having : )

  5. Hi,
    I’m trying to get the load more button to work for my page.
    But I’m getting this error: “Uncaught ReferenceError: ajax_posts is not defined”
    I used localized script after the script.js has been added.

    Do you have an idea what I’m doing wrong?

  6. Hi Nathan
    You helped me with this a few months ago (see above) and I have since used this script on a number of sites as I love it, it works perfectly, until now… I have a site that is set to 12 posts per page in the blog query, the JS and the PHP file, except when I click load more it only loads every 3rd post. I have also set all to 3 posts per page, i get the first 3, when I click load more it gives me the 6th, click again and I get the 9th etc..
    Any ideas?

      1. Any chance you could drop me an email please? Would be happy to let you log in to look at the theme I am developing so you can see the code I use in load-more .js and .php

        Many thanks

  7. Hi Nathan. Firstly, thanks for sharing this code. I’m trying to get this code work for my category page and custom query located in the front-page of my blog (ceptekno.com). function.js file gives me a reference error in my case for the line
    > var str = ”&pageNumber=’ + pageNumber + ‘&ppp=’ + ppp + ‘&action=more_post_ajax’;.

    The error message I get is > left hand side of operator ‘=’ must be a reference

    All in all I could not get his work in my project:( maybe a bit of help?

  8. I have followed the tutorial (great btw) but when I press ‘load more’ the entire website loads inside the section where the next page titles should appear….

  9. Hi,
    Thank you so much for the tutorial. Been looking for this since ages and finally i found a working code! My only problem is how to load 9 post initially and 3 more post each click on the load more? Can you help me please?

  10. Nathan, thanks for this post – exactly what I needed for a current project.

    Is there a way to only have the “load more” button appear once on a page? I’m currently loading the 6 most recent posts and I want to ‘hide’ the next 8 posts with the load more button. Once those 8 posts are loaded, I don’t want that button to appear any longer.

    I’ve tried removing $(this).insertAfter(‘#ajax-posts’); from the script but that doesn’t seem to do the trick.

    Any suggestions?

    1. For this you can use the following jQuery code:

      
      $(".element").one("click", function(event) {
          //do something
      });
      

      This will execute on the first click and disable for the second click, you can always use the $('.element').hide(); function to hide the button after click…

      Hope that helps ( :

  11. Hi Nathan
    Hope you’re well.
    Using this on a new site which has 10 posts per page, currently 16 posts published. Once button is clicked, final 6 posts load but button stays on the page, i’d like to hide it if there are no further posts to be loaded.
    Tried to change:
    else{
    $(“#more_posts”).attr(“disabled”,true);
    }
    to:
    else{
    $(“#more_posts”).hide();
    }
    but that didn’t affect anything. Any help appreciated.
    Thanks, Adrian

    1. Hi Adrian,

      Can you try changing the JS to this:

      
      success: function(data){
          var $data = $(data);
          if($data.length){
              $("#ajax-posts").append($data);
              $("#more_posts").attr("disabled",false); // You can remove this line
              $("#more_posts").hide(); // Add the Hide Button JS here
          } else{
              $("#more_posts").attr("disabled",true);               
          }
      },
      

      Let me know if that works.

  12. I’m getting an error ‘Uncaught ReferenceError: ajax_posts is not defined’. I’ve checked my functions.php file and the wp_localize_script is there, the only thing I have changed in that section is theme at the end from twentyfifteen to my site’s theme (not even sure if this is necessary).

    wp_localize_script( ‘core-js’, ‘ajax_posts’, array(
    ‘ajaxurl’ => admin_url( ‘admin-ajax.php’ ),
    ‘noposts’ => __(‘No older posts found’, ‘rima-child’),
    ));

    I don’t think it would make a difference that I’m using a child theme, I’m also using ACF to control the number of posts being shown per page.

  13. Thanks you Nathan but this is not working for me.

    got error : ReferenceError: ajax_posts is not defined
    at load_posts (functions.js?ver=5.5.5:187)

    I don’t understand this part :
    NOTE: You will need to replace code-js with the file you are going to be adding the JS code later in this tutorial.

    Which js code ? the code added in function.js ?

    Thanks for your help

  14. Just trying out the infinite scroll method instead of the onClick for the first time, got it loading the posts when I scroll to the footer which is great however it loads the next 10 posts then doesn’t stop, and after a few seconds it has loaded all 200+ posts without any further scrolling.
    JS is:
    $(window).on(‘scroll’, function () {
    if ($(window).scrollTop() + $(window).height() >= $(‘#footer’).height() – 100) {
    load_posts();
    }
    });

    Thanks.

  15. Following on from post above (infinite scroll issue), I added some console.log messages to see what’s going on, and it looks like it is actually firing the function and loading 10 posts for every pixel past that 100 from bottom, so if I scrolled to 95px from bottom then it will load 10 posts x 5 pixels (50 posts).

  16. Hi Nathan – big thanks for posting this and the detailed explanation! It’s great.

    I was wondering is it fairly easy to keep the Load More button visible once all posts are loaded (I can see it has display: none added when there’s no more posts to load) but just add a class so I can use CSS to style it so it still looks disabled?

    So instead of

    Load More

    Something more like;

    Load More

    Many thanks,
    T

    1. You will see there is already a line of code commented out: //$("#more_posts").attr("disabled",false); // Uncomment this if you want to disable the button once all posts are loaded.

      Just uncomment this, and remove the $("#more_posts").hide();

      Then just ensure you have styles added for your :disabled selector.

  17. Also another question, is it possible to have 3 different versions of this with slightly different queries on a single page?

    I can see the ID “ajax_posts” is mentioned in the function, so I’m guessing not?

    Thanks,

  18. Hi Nathan
    Just wondering if you had any thoughts about my comment from the 7th regarding the infinite scroll, and it firing the function for every pixel scrolled, can you replicate this? Would you like the URL where i’ve placed this to see where it happens?
    Looking forward to your feedback. Thanks.

  19. It doesnt work.

    I did everything in tutorial and it did nothing. Button is just plain text and do nothing.

Join the discussion

Related Posts

Related - ACF Repeater Load More using AJAX

Wordpress / 21st June 2021

ACF Repeater Load More using AJAX

Read More Related - How to Skip the Cart Page and Go Straight to the Checkout Page in WooCommerce

WooCommerce / 17th September 2020

How to Skip the Cart Page and Go Straight to the Checkout Page in WooCommerce

Read More