Implementing an AJAX pagination system in WordPress is usually achieved by fetching the HTML content of every page linked by the pagination elements. This is the wrong way to implement this feature. Instead, we can get better results by using the WordPress AJAX APIs.
We only need the page number of each link. For that purpose, we can create our custom AJAX action in functions.php
:
function my_get_posts_for_pagination() {
$paged = $_GET['page']; // Page number
$html = '';
$pag = 0;
if( filter_var( intval( $paged ), FILTER_VALIDATE_INT ) ) {
$pag = $paged;
$args = array(
'paged' => $pag, // Uses the page number passed via AJAX
'posts_per_page' => 2 // Change this as you wish
);
$loop = new WP_Query( $args );
if( $loop->have_posts() ) {
while( $loop->have_posts() ) {
$loop->the_post();
// Build the HTML string with your post's contents
}
wp_reset_query();
}
}
echo $html;
exit();
}
add_action( 'wp_ajax_my_pagination', 'my_get_posts_for_pagination' );
add_action( 'wp_ajax_nopriv_my_pagination', 'my_get_posts_for_pagination' );
We need a visual loading indicator in order to show that an action is in progress. A pure CSS loader can be implemented as follows:
/*= Pagination loader */
#pagination-loader {
width: 40px;
height: 40px;
margin: 1.5em auto;
background: #ccc;
border-radius: 50%;
-webkit-animation: pulse 1s infinite alternate;
animation: pulse 1s infinite alternate;
display: none;
}
@-webkit-keyframes pulse {
from {
opacity: 1;
width: 40px;
height: 40px;
}
to {
opacity: 0.5;
width: 35px;
height: 35px;
}
}
@keyframes pulse {
from {
opacity: 1;
width: 40px;
height: 40px;
}
to {
opacity: 0.5;
width: 35px;
height: 35px;
}
}
In jQuery we need to get the page number of the current link. This can be done with regular expressions. Once the posts are fetched via AJAX, we have to mark the current link in order to avoid the insertion of duplicated posts.
Our AJAX action sends two parameters through the GET request: the name of the WordPress action and the current page number.
(function( $ ) {
$.fn.wpPagination = function( options ) {
options = $.extend({
links: "a",
action: "my_pagination",
ajaxURL: "http://" + location.host + "/wp-admin/admin-ajax.php",
next: ".next",
previous: ".previous",
disablePreviousNext: true
}, options);
function WPPagination( element ) {
this.$el = $( element );
this.init();
}
WPPagination.prototype = {
init: function() {
this.createLoader();
this.handlePreviousNextLinks();
this.handleLinks();
},
createLoader: function() {
var self = this;
self.$el.before( "<div id='pagination-loader'></div>" );
},
handlePreviousNextLinks: function() {
var self = this;
var $previous = $( options.previous, self.$el );
var $next = $( options.next, self.$el );
if( options.disablePreviousNext ) {
$previous.remove();
$next.remove();
} else {
$previous.addClass( "clicked" );
$next.addClass( "clicked" );
}
},
handleLinks: function() {
var self = this,
$links = $( options.links, self.$el ),
$loader = $( "#pagination-loader" );
$links.click(function( e ) {
e.preventDefault();
var $a = $( this ),
url = $a.attr( "href" ),
page = url.match( /\d+/ ), // Get the page number
pageNumber = page[0],
data = {
action: options.action, // Pass the AJAX action name along with the page number
page: pageNumber
};
if( !$a.hasClass( "clicked" ) ) { // We don't want duplicated posts
$loader.show(); // Show the loader
$.get( options.ajaxURL, data, function( html ) {
$loader.hide(); // Hide the loader
$loader.before( html ); // Insert posts
$a.addClass( "clicked" ); // Flag the current link as clicked
});
}
});
}
};
return this.each(function() {
var element = this;
var pagination = new WPPagination( element );
});
};
})( jQuery );
We can use the above code as follows:
(function( $ ) {
$(function() {
if( $( "#pagination" ).length ) {
$( "#pagination" ).wpPagination();
}
});
})( jQuery );
You can see the demo below.