WordPress: Load more posts on page scroll

Sweet, sweet AJAX goodness!
Sweet, sweet AJAX goodness!

Note: This article has been marked for a quality review and will soon be updated.

Although I don't visit archive pages very often, when I do I like it when I don't have to trawl through pages and pages of 10 posts using tiny navigation buttons at the bottom of the page. A great way to prevent this annoyance for your users is to use AJAX to dynamically append posts to the end of your archive list. To do this in WordPress we need to abstract the process of getting the list of posts and then use some Javascript (in out case jQuery aided) magic to load in our content. So lets get started.

First off we need to create a function that we can call to spit out a list of posts. Usually on an archive page we would use the normal WordPress loop to accomplish this, but in our case we need to abstract the loop into a separate function so that we can access it from any file - more specifically our Javascript file. We also need to be able to pass an offset value to this function to make sure we don't load the same posts every time it is called. This function is going to differ slightly depending on your markup for your archive page, but this is how it would look for Tom's Big Box:

function getArchives($count,$offset){
	query_posts('posts_per_page='.$count.'&offset='.$offset);
	
	$posts = array();

	if(have_posts()) : while(have_posts()) : the_post(); 	
		$img = post_image($post->ID);
		$commentsNo = get_comments_number();
		if($commentsNo==0){
			$comments = 'No Responses';
		}else if($commentsNo==1){
			$comments = '1 Response';
		}else {
			$comments = $commentsNo . ' Responses';
		}
		
		$item = '<li id="'. get_the_ID() .'">
			<figure class="vignette left">
				<img src="'. get_bloginfo('template_url') .'/scripts/timthumb.php?src='. $img->src .'&w=180&h=120" alt="'. $img->alt .'" />
			</figure>
			<h3><a href="'. get_permalink() .'">'. get_the_title() .'
			<p class="article-meta left">'. get_the_time('jS M Y',$post->ID) .' ~ '. $comments .'

<p class="extract"> '. get_the_excerpt() .' </p> </li>'; array_push($posts,$item); endwhile; endif; return $posts; }*/

Stick this in your functions.php file, and we can walk through what this code is doing. Firstly the two parameters that we it accepts are $count - which tells our functions how many posts we want, and $offset - to ensure that we don't load the same posts every time our function is called. We then send these parameters to WordPress's query_posts() function which will give us a list of posts. We then set up the $posts array to sold our data and begin iterating through the posts. I created a separate image variable to hold information about each post's image because I don't use WordPress's inbuilt image resizing, this is something which may not apply to you. After that an if statement creates a nice string based on the number of comments per post - this is because the inbuilt functions return only the number of comments or directly outputs to the document - something we don;t want. Then we load our content into the $item variable using whatever markup we need, and finally use array_push() to add our $item variable to the $posts variable, before returning the final $posts variable in its entirety.

Phew! Well that's most of the server-side stuff done, but before we can say goodbye to PHP, we need to do one more thing. We need to create a file to which we will pass our parameters, and that will execute the function we just created, and return the code we need. Don't worry, this is far simpler than the function above, let's take a look at the code:

require( '../../../../wp-load.php' );
$posts = getArchives(20,$_GET['offset'],false); 
foreach($posts as $p){
	echo str_replace("'","'",$p);
}

So, ignoring the first line for a moment, this file simply passes the $_GET['offset'] variable to our newly created function, and then iterates over the output, making sure to replace any single quotation marks to stop it from breaking. "But hang on Tom, what does that first line do?!" I hear you cry, well the first line makes sure we can access the function we just created, and for that matter all the inbuilt WordPress functions, so it is vital that you keep it. You will also need to change the path in the require() statement to ensure it matches the root directory of your WordPress install. Pop the above code in a file called get-posts.php and stick it in your theme's directory. And with that, we can say hasta la vista to PHP, and buongiorno to jQuery (that's right, I'm cultured).

Now, having made sure jQuery is included on your page, paste in the following code:

var halfWay = ($(document).height()/2);
var offset = 20;
var inAction = false;
var reachedEnd = false;

$(document).scroll(function(){
   if($(document).scrollTop() > halfWay && inAction == false){
       inAction = true;
		
       $('.article-list').append($('
').load('http://example.com/wp-content/themes/myTheme/get-posts.php?offset='+offset, function(){ offset = offset + 20; halfWay = halfWay + 1775; inAction = false; })); } });

Lets go through what this does. We have a function that fires whenever the page scrolls, and within that we compare the current scroll amount to half the overall document height, if it is greater (meaning that the user has scrolled more than half way down the page), and the variable inAction (I'll get to that in a moment) is true, it executes the following code. If the conditions are satisfied we then use AJAX to load in some more posts. Now it may appear a rather convoluted way to be doing so, but in my experience this way of handling an AJAX request of this type is the best. So for this method we send a request to our file that we just created in our theme's directory, and pass it the variable offset, when the content is loaded we run the embedded function which increments our offset variable by 20, adds a suitable amount to our halfWay variable. We then reset the inAction variable. This variable is used to prevent a massive build up of requests because of page scrolling, we use it because every time the page scrolls even a pixel, the function is called, so we don't want it to be called 3 times before we have a chance to offset the next post, otherwise we will end up with a huge amount of duplicate posts being loaded in. The conditions in the if statement will stop being satisfied after the last posts are loaded in because our inAction will stay in it's true state forever after one request for no posts means the success() function isn't executed.

And that's it! It might be a good idea to have a loading graphic at the bottom of you page when doing this to give some user feedback, and I would suggest also adding pagination just incase your server stops responding and leaves your users hanging. But aside from that, I think I covered the basics of how to implement the AJAx loading in of content. Oh and by the way, you will now also be able to generate archives anywhere on your site using the function getArchives(), meaning you will write less code in the long run - two birds with one stone!

***

If you found this particularly useful and want to share the ♥ you can donate here.

***

16 Responses

  1. Mustafa A.

    I've been searching for this method for a long time. Thank you so much!

    Everything I understand till the point of the last code.
    So, where exactly should I implement the last code?

    Reply
    • Tom

      You would put this in the footer of your page, and as it is Javascript, between the <script> tags.

      Reply
      • Mustafa A.

        Somehow, the js won't load the posts from get-posts.php , Even though get-posts.php works fine when I directly opened it but the append load method within that js code is not working. Any help?

        Reply
        • Mustafa A.

          So I'm doing all this inside a post, I mean I'm having the archive within that post, so is that the problem?

          Reply
          • Tom

            You should have your archive.php page loading in posts via AJAX from get-posts.php, and the JS should be in the footer of the archive.php page. Does that make sense?

  2. Mustafa A.

    How is the HTML code looks like of the archive.php page?
    I'm sure there is something wrong with my code, please advise :)

    Reply
    • Mustafa A.

      I got it working finally!
      The problem was the value of halfWay = halfWay + 1775;
      was too high and I set it it 1000 and it worked.
      Thank you so much!!

      Reply
  3. Carlos

    Tried, but can't make it to work. Im sure I lost in some step, but not sure where...

    Reply
    • Tom

      Think you can isolate the problem to either the PHP or JavaScript?

      Reply
  4. Lee Mason

    Thanks for this, its exactley what i need for my new site (gonna wait until i have enough posts on it before i start it though).

    one thing i would say to bring it up to wp security standards and to use ajax the way wp likes is to us the admin-ajax.php file rather than having a theme file with wp-load.php.

    the function wouldnt change really, just hook it to admin ajax with the right actions and change the ajax url in the js.

    you need to use the ajax hooks which are well explained here:

    http://www.garyc40.com/2010/03/5-tips-for-using-ajax-in-wordpress/

    Reply
  5. Laurens

    Thanks! Great method!

    Reply
  6. webdwall

    Awesome! works like a charm.. Thank you.
    For some reason the 'load' javascript woudn't work in my case. I replaced 'load' with jQuery ajax and it worked..

    Reply
  7. Coz

    Just wanted to say this was useful and well written (besides a couple of typos :p ) - thanks for the tip about the file addressing, snagged on those. I have a couple of small and possibly related questions - How do we know when there are no more, and what do we do with reachedEnd ?

    Reply
  8. coz

    Any chance you could show us how to use those hook in THIS example? It looks like we need 3 more files, and then to call the myajax_submit() somehow? confused! :)

    Reply
  9. DroidJam

    Nice and awesome post, thanks

    Reply

~ Add a response ~

Please ensure you have filled in all fields with appropriate information!