I've been thinking of using WP_Query's pagination to make a custom lazy loading. is there a way to utilize the WP_Query's pagination for a lazy load?

Say for example, I have to load 24 posts in the first load, and when the scroll reach bottom it will lazy load the next 24 post.

Is this possible?

  • What about AJAX? When you scroll down you just fetch 24 more posts. – denis.stoyanov Feb 19 '16 at 10:09
  • @denis How to do that using AJAX? i really have no idea, i'm literally a newbie – karlo jay bueno Feb 19 '16 at 10:10
  • @denis.stoyanov is correct. There are a couple of good ajax loaders. Do a google search for ajax infinite scroll. – Pieter Goosen Feb 19 '16 at 10:12
  • 1
    If so, start adding a example of your Ajax implementation, working or not, that you are trying to integrate with WP_Query and explain where exactly do you have problems. Otherwise, your question "Is this possible?" has a simple answer: "Yes, it is". I hope you understand that you need to narrow down to specific problems to get a good answer. Do you know how to implement Ajax within WordPress? Do you know how to work with paginated results in WP_Query? – cybmeta Feb 19 '16 at 10:39
  • 1
    @cybmeta, thank you. i will post a specific question for this if i encounter a problem – karlo jay bueno Feb 19 '16 at 10:48

Take a look at this rough example. This requires a little bit of adaptation but as a whole, it does what you want - it loads X amount of next posts if user clicks a button which should be below already loaded posts.

If you want to automatically load more posts if user scrolls down, just replace click event with some other code that keeps an eye on scrolling. There's plenty of examples online.

  • Keep an eye on jQuery('some-html-element')-s, make sure to rename these element names or change your own HTML to make them fit
  • Total posts count: you can make it visible if you want users to see total posts count or use CSS opacity to hide it. It still needs to be somewhere in order to have a place to store the value

This goes to your main .js:

This function handles all the DOM manipulation and ajax. It can be called however you wish.

//ajaxLock is just a flag to prevent double clicks and spamming
var ajaxLock = false;

if( ! ajaxLock ) {

    function ajax_next_posts() {

        ajaxLock = true;

        //How many posts there's total
        var totalPosts = parseInt( jQuery( '#total-posts-count' ).text() );
        //How many have been loaded
        var postOffset = jQuery( '.single-post' ).length;
        //How many do you want to load in single patch
        var postsPerPage = 24;

        //Hide button if all posts are loaded
        if( totalPosts < postOffset + ( 1 * postsPerPage ) ) {

            jQuery( '#more-posts-button' ).fadeOut();

        //Change that to your right site url unless you've already set global ajaxURL
        var ajaxURL = 'http://www.my-site.com/wp-admin/admin-ajax.php';

        //Parameters you want to pass to query
        var ajaxData = '&post_offset=' + postOffset + '&action=ajax_next_posts';

        //Ajax call itself

            type: 'get',
            url:  ajaxURL,
            data: ajaxData,
            dataType: 'json',

            //Ajax call is successful
            success: function ( response ) {

                //Add new posts
                jQuery( '#posts-container' ).append( response[0] );
                //Update the count of total posts
                jQuery( '#total-posts-count' ).text( response[1] );

                ajaxLock = false;

            //Ajax call is not successful, still remove lock in order to try again
            error: function () {

                ajaxLock = false;

This goes to your main .js:

This is an example how to call function above with button, this is better in my opinion, user can choose if he/she wants to see more..

//Load more posts button
jQuery( '#more-posts-button' ).click( function( e ) {




This goes to functions.php or create a mu-plugin:

This is the function that "runs" in your server, ajax calls this, it does it's thing and sends results back.

//More posts - first for logged in users, other for not logged in
add_action('wp_ajax_ajax_next_posts', 'ajax_next_posts');
add_action('wp_ajax_nopriv_ajax_next_posts', 'ajax_next_posts');

function ajax_next_posts() {

    //Build query
    $args = array(
        //All your query arguments

    //Get offset
    if( ! empty( $_GET['post_offset'] ) ) {

        $offset = $_GET['post_offset'];
        $args['offset'] = $offset;

        //Also have to set posts_per_page, otherwise offset is ignored
        $args['posts_per_page'] = 24;

    $count_results = '0';

    $query_results = new WP_Query( $args );

    //Results found
    if ( $query_results->have_posts() ) {

        $count_results = $query_results->found_posts;

        //Start "saving" results' HTML
        $results_html = '';

        while ( $query_results->have_posts() ) { 


            //Your single post HTML here

        //"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, $count_results );
    echo json_encode( $response );

    //Always use die() in the end of ajax functions
  • Thanks @N00b, I will be trying your code and see if it works. :) I'll get back to you soon, – karlo jay bueno Feb 19 '16 at 14:04
  • It works 100%, I tested it a little bit. You'll just have to adapt it correctly. – N00b Feb 19 '16 at 14:07
  • Yeah. Thank you @N00b, but i still have to modify it to fit my layout. By the way, How can Integrate the posts retrieved with my layout? – karlo jay bueno Feb 19 '16 at 14:10
  • 1
    Im glad it's working. I also hope that you learned something in the process. – N00b Feb 20 '16 at 0:45
  • 1
    Yes, i did learn a lot. THANKS agaim @N00b. You're wonderful. – karlo jay bueno Feb 20 '16 at 1:46

I've been using infinite scroll for this.

Here is what I have used in script.js

    var $container = $('#ms-container');
        itemSelector : '.ms-item',

//var templateUrl = object_name.templateUrl;
//var src =  "'"+templateUrl+"/images/loader.gif' ";

      navSelector  : '#navigation',    // selector for the paged navigation 
      nextSelector : '#navigation a',  // selector for the NEXT link (to page 2)
      itemSelector : '.ms-item', 

      loading: {
          finishedMsg: $('<div class="finmsg">No More Posts.</div>'),
          msgText: '',
          img: '',
          speed: 0
      // trigger Masonry as a callback
      function( newElements ) {
        // hide new items while they are loading
        var $newElems = $( newElements ).css({ opacity: 0 });
        // ensure that images load before adding to masonry layout
          // show elems now they're ready
          $newElems.animate({ opacity: 1 });
          $container.masonry( 'appended', $newElems, true ); 

I am using masonry, so you might need to tweak this. The overall container on the archive page is

<div id="ms-container">


var $container = $('#ms-container');

My container for each individual post displayed on my archive page is:

<div class="ms-item col-sm-4">


itemSelector : '.ms-item',

This is what I am using as the pagination at the bottom of the archive page:

      <div id ="navigation" class="pagination pull-left prevnext">
      <ul class="list-inline clearfix">
        <li class="text-left pull-left"><?php previous_posts_link( '<span class="glyphicon glyphicon-menu-left" aria-hidden="true"></span> Previous' );?></li>
        <li class="text-right pull-right"><?php next_posts_link( 'Next <span class="glyphicon glyphicon-menu-right" aria-hidden="true"></span>'); ?></li>

If I remember correctly this is where I got the info on how to do it: https://stackoverflow.com/questions/9766515/imagesloaded-method-not-working-with-jquery-masonry-and-infinite-scroll


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.