Load Old WordPress Posts on the Same Page with AJAX

Loading a new page to see content is annoying and time consuming, so why would you want to make visitors to your site load a new page to see old WordPress posts? With the following instructions, you can build a plugin that replaces the “Older Posts” link with a button on the bottom of the page that loads older WordPress posts on the same page.

If you want, you can just download the completed plugin here. (Just upload and activate it.)

The plugin is going to have a few main features:

  • Each button click will load another page of posts. (i.e. first will open page 2’s old posts, second will load page 3’s posts, etc.)
  • It will check for WordPress posts first and tell the users if there are no more.
  • If the visitor doesn’t have JavaScript, it will default to the old format.

Plugin Structure

The plugin will have 3 files: a PHP one, a CSS one, and a JS one.

File Structure

Paste the snippet below into the top of the pbd-ajax-load-posts.php file:

<!--?php
/**
 * Plugin Name: PBD AJAX Load Posts
 * Plugin URI: http://www.problogdesign.com/
 * Description: Load the next page of posts with AJAX.
 * Version: 0.1
 * Author: Pro Blog Design
 * Author URI: http://www.problogdesign.com/
 * License: GPLv2
 */-->

Load Files and Pass Values

We need to make sure the JavaScript and CSS files are loaded onto the right pages. Paste the code below into pbd-ajax-load-posts.php:

/**
  * Initialization. Add our script if needed on this page.
  */
 function pbd_alp_init() {
 	global $wp_query;

 	// Add code to index pages.
 	if( !is_singular() ) {	
 		// Queue JS and CSS
 		wp_enqueue_script(
 			'pbd-alp-load-posts',
 			plugin_dir_url(__FILE__) . 'js/load-posts.js',
 			array('jquery'),
 			'1.0',
 			true
 		);

 		wp_enqueue_style(
 			'pbd-alp-style',
 			plugin_dir_url(__FILE__) . 'css/style.css',
 			false,
 			'1.0',
 			'all'
 		);

The code above creates a function, pbd_alp_init(). The global $wp_query variable is called and will be used in the next step.

The first statement on line 8 is a conditional statement. On any page that isn’t an individual post or Page, we’re going to run this code. This is to make sure it works on the hompages, search pages, etc. You can change it so it’s more specific.

wp_enqueue_script() and wp_enqueue_style() are then used to tell WordPress about the JavaScript and CSS files and the fact that we’re using jQuery.

Now some values need to be passed to the script:

  • The page number we’re on.
  • The total number of pages.
  • The link to the next page.

The wp_localize_script() function will calculate each of these values in PHP and print them to the webpage so the script can access them later.

// What page are we on? And what is the pages limit?
$max = $wp_query->max_num_pages;
$paged = ( get_query_var('paged') > 1 ) ? get_query_var('paged') : 1;

// Add some parameters for the JS.
wp_localize_script(
	'pbd-alp-load-posts',
	'pbd_alp',
	array(
		'startPage' => $paged,
		'maxPages' => $max,
		'nextLink' => next_posts($max, false)
	)
);

We begin with finding the maximum number of pages and the page we’re on.

$max is the maximum number of pages the current query can return. (e.g. If there are 18 WordPress posts and each page shows 6 posts, then the max is 3.)

The $paged variable will store the page we’re currently on.

Line 12 finds the link to the next page with WordPress’s next_posts() function which returns the needed URL.

The first value, ‘pbd-alp-load-posts’, must match the first value in the wp_enqueue_script() call. The second value, ‘pbd_alp’, will be the name used in the JavaScript file.

Finally, an array of the data is sent.

Now we just need to close up the if statement and hook it into place:

 	}
 }
 add_action('template_redirect', 'pbd_alp_init');

The template_redirect hook is used because with the init hook the $wp_query variable won’t be set yet.

jQuery

Open the load-posts.js file. The first thing to do is access the 3 variables passed in the PHP function.

jQuery(document).ready(function($) {

	// The number of the next page to load (/page/x/).
	var pageNum = parseInt(pbd_alp.startPage) + 1;

	// The maximum number of pages the current query can return.
	var max = parseInt(pbd_alp.maxPages);

	// The link of the next page of posts.
	var nextLink = pbd_alp.nextLink;

The way to access the values is to use the format: pbd_alp.valueName.

The numbers have been sent over as strings so JavaScript’s parseInt() function is needed to convert them back to numbers.

pageNum adds one to the number because it is going to store the number of the next page to load. The first thing to do is get rid of any “Older Posts” navigation links and insert the AJAX button instead.

/**
 * Replace the traditional navigation with our own,
 * but only if there is at least one page of new posts to load.
 */
if(pageNum <= max) {
	// Insert the "More Posts" link.
	$('#content')
		.append('+ pageNum +'">')
		.append('

Load More Posts

');

	// Remove the traditional navigation.
	$('.navigation').remove();
}

We start out with a conditional check. pageNum is the number of the next page, so if it’s greater than max, the button shouldn’t be displayed.

If there is new content to be loaded, it searches for the #content div and add an empty div to insert posts into and the button itself wrapped in a paragraph.

Finally we look for the .navigation div and remove it. If your theme uses a different class for the navigation buttons or the content, make sure to use those.

So the button is made, but it doesn’t do anything. Now it will:

/**
 * Load new posts when the link is clicked.
 */
$('#pbd-alp-load-posts a').click(function() {

	// Are there more posts to load?
	if(pageNum <= max) {

		// Show that we're working.
		$(this).text('Loading posts...');

The first line is a jQuery event handler, which runs when a user clicks the button.

Line 7 runs a check to make sure that pageNum is less than max so we don’t get any 404 errors.

Line 10 updates the text on the button to say “Loading posts…”

Now to make the AJAX call:

$('.pbd-alp-placeholder-'+ pageNum).load(nextLink + ' .post',
	function() {
		// Update page number and nextLink.
		pageNum++;
		nextLink = nextLink.replace(/paged[=].[0-9]?/, 'paged='+ pageNum);

		// Add a new placeholder, for when user clicks again.
		$('#pbd-alp-load-posts')
			.before('+ pageNum +'">')

		// Update the button message.
		if(pageNum <= max) {
			$('#pbd-alp-load-posts a').text('Load More Posts');
		} else {
			$('#pbd-alp-load-posts a').text('No more posts to load.');
		}
	}
);

The first line uses a jQuery selector to select our placeholder div. The pageNum number is added to the end of the class to ensure that the old placeholder isn’t overwritten.

The .load() function loads the URL we pass and because of the .post added to the end, it will only copy the .post divs.

Line 2 starts a new function which will run the AJAX call completes. The first thing it does is update the values for the next button click.

pageNum is increased by one, and nextLink is updated with a regular expression. This searches the URL for /page/(some number)/, and replaces it with number with the new next page.

Line 8 adds a new placeholder div for the next button click.

Line 12 updates the text on the button again. If there are more posts, it will revert to the original message. If not, it will say so.

Now to close things up:

		} else {
			$('#pbd-alp-load-posts a').append('.');
		}	

		return false;
	});
});

This ends the first if statement. If there are no more pages to load, it adds a ‘.’ to the button’s message. Last of all, we use the return false; to prevent the link from the button itself from loading.

Make it Look Good

Now to make your button look how you want it to with CSS. Add the following code to your css/style.css file for the button in the following example picture:

No More Posts

#pbd-alp-load-posts a:link, #pbd-alp-load-posts a:visited {
	display: block;
	text-align: center;
	padding: 4px 0;
	color: #444;
	text-decoration: none;

	/** Rounded Corners **/
	-moz-border-radius: 8px;
	border-radius: 8px;

	/** Drop shadow **/
	-moz-box-shadow: 1px 1px 1px #999;
	-webkit-box-shadow: 1px 1px 1px #999;
	box-shadow: 1px 1px 1px #999;

	/** Gradients : http://css-tricks.com/css3-gradients/ */
	/* fallback */
	background-color: #f1f1f1;

	/* Firefox 3.6+ */
	background: -moz-linear-gradient(100% 100% 90deg, #e4e3e3, #f1f1f1);

	/* Safari 4-5, Chrome 1-9 */
	/* -webkit-gradient(,  [, ]?,  [, ]? [, ]*) */
	background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#f1f1f1), to(#e4e3e3));

	/* Safari 5.1+, Chrome 10+ */
	background: -webkit-linear-gradient(#f1f1f1, #e4e3e3);

	/* Opera 11.10+ */ background: -o-linear-gradient(#f1f1f1, #e4e3e3);
}

#pbd-alp-load-posts a:hover, #pbd-alp-load-posts a:active {
	/** Drop shadow **/
	-moz-box-shadow: 1px 1px 1px #bbb;
	-webkit-box-shadow: 1px 1px 1px #bbb;
	box-shadow: 1px 1px 1px #bbb;

	/** Gradients : http://css-tricks.com/css3-gradients/ */
	/* fallback */
	background-color: #f5f5f5;

	/* Firefox 3.6+ */
	background: -moz-linear-gradient(100% 100% 90deg, #eaeaea, #f5f5f5);

	/* Safari 4-5, Chrome 1-9 */
	/* -webkit-gradient(,  [, ]?,  [, ]? [, ]*) */
	background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#f5f5f5), to(#eaeaea));

	/* Safari 5.1+, Chrome 10+ */
	background: -webkit-linear-gradient(#f1f1f1, #eaeaea);

	/* Opera 11.10+ */ background: -o-linear-gradient(#f5f5f5, #eaeaea);
}

Now just save your work and activate the plugin and you’re in business!

(Info from: Pro Blog Design)