You may want to add a modern look and feel for the WordPress pagination style, like a Load more button or an infinite scroll effect.
You could try getting this effect using WP AJAX
by creating a new Query and populating it with arguments.
Still, if you’re like me, you’ll feel like it’s wrong, bizarre, and messy to handle a lengthy request to get to the next page of posts, with a massive list of parameters and every possible scenario that posts can also have, given the fact you that you want this to work with categories and slugs like tags and sometimes even with custom posts.
I know it’s a lot of headaches doing this without using any plugins, but again, if you’re like me, I try to stray away from plugins to gain knowledge, and if something goes wrong, I’ll be familiar with my code base.
The path I chose
My approach to solving this puzzle is that I want the code to be DRY.
So, I know that in the standard HTTP request, WordPress is already doing the heavy lifting for us with every request, so I needed to understand how WordPress handles the request, and then I’ll disguise the Ajax request as an HTTP request to get back the results sound easy? Yes, it is.
Mocking the request
Since we need to work with the main query, The plan is to make WordPress think it’s a standard HTTP request and get back the results.
So I dag down in WordPress source code.
function wp( $query_vars = '' ) {
global $wp, $wp_query, $wp_the_query;
$wp->main( $query_vars );
if ( ! isset( $wp_the_query ) ) {
$wp_the_query = $wp_query;
}
}
PHP- The main is set up by
wp()
function inwp-includes/functions.php
. - a call to
$wp->main()
function in theWP Class
is what creates the main query.
public function main( $query_args = '' ) {
$this->init();
$parsed = $this->parse_request( $query_args );
$this->send_headers();
if ( $parsed ) {
$this->query_posts();
$this->handle_404();
$this->register_globals();
}
/**
* Fires once the WordPress environment has been set up.
*
* @since 2.1.0
*
* @param WP $wp Current WordPress environment instance (passed by reference).
*/
do_action_ref_array( 'wp', array( &$this ) );
}
wp-includes/class-wp.php view on Github- The
parse_request
function is what writes or rewrites the main query by parsing the request and finds the correct WordPress Query.
list( $req_uri ) = explode( '?', $_SERVER['REQUEST_URI'] );
$self = $_SERVER['PHP_SELF'];
wp-includes/class-wp.php view on Github- By handling specific request parameters like
$_SERVER['PHP_SELF'];
and$_SERVER['REQUEST_URI']
and with just these two properties we can trick WordPress into building the query.
$_SERVER properties ‘PHP_SELF’ and ‘REQUEST_URI’
- The
PHP_SELF
is the filename of the currently executing script, and we know that the page generating the posts is alwaysindex.php
. - The
REQUEST_URI
is the URI given to access this page, e.g. ( for posts is/
and for tags/tag/100daysofcode
), we can get this information form the client javascript by Ajax and send the current URL path as a parameter.
Let’s create an Ajax
function load_more_handler() {
$page = $_POST['currentPage'] + 1;
$path = $_POST['path'];
// * mock index.php call
$_SERVER['REQUEST_URI'] = $path;
$_SERVER['PHP_SELF'] = '/index.php';
wp(array('paged' => $page, 'post_status'=> array('publish')));
if (have_posts()) {
ob_start();
while (have_posts()) {
the_post();
get_template_part('template-parts/article');
}
wp_send_json_success(ob_get_clean());
} else {
wp_send_json_success(array('done'=>True));
}
}
add_action('wp_ajax_load_more_all', 'load_more_handler');
add_action('wp_ajax_nopriv_load_more_all', 'load_more_handler')
PHPThe Ajax request should hold two parameter; The two parameters are currentPage
and path
The currentPage
parameter is just the current page number and we can inject it by PHP to the HTML as data-attribute, then get with JavaScript and send it with the request.
The path
parameter it’s the path name of the current page and we can get it in JavaScript by window.location.pathname
and send it with the request. (see the js code)
Set the properties
$_SERVER['REQUEST_URI'] = $path;
$_SERVER['PHP_SELF'] = '/index.php';
Finally we know we need to call the wp()
function to build the main query, and we passed an array of arguments
wp(array('paged' => $page, 'post_status'=> array('publish')));
Note that we strictly asked the main query only to show us the published posts because “if the Query is run in an admin context (administration area or AJAX call), protected statuses are added too.”
You can read more about the status parameters here.
Creating the loop to get the posts using ob_start()
and ob_get_clean()
.
you can read more about the Output Control Functions in the PHP docs here.
Also, it’s better if you’d like create a template for a single post layout and get it using get_template_part
to maintain clean code.
The Javascript client code
let url = '/wp-admin/admin-ajax.php';
let action = 'load_more_all';
// this is set by php get_query_var('paged');
let currentPage = $('#articles').data('currentpage');
let path = window.location.pathname;
// this function increments the html data-attribute
funciton nextPage() {
$('#articles').data('page', parseInt(this.currentPage) + 1);
}
$.ajax({
type: 'POST', url, data: { action, currentPage, path },
success: function (res) {
nextPage();
$('#articles').append(res.data);
},
error: function (err) { console.error(err); }
});
JavaScriptYou can see it in action on the current site
I hope you enjoyed reading it. I would love to hear your thoughts.
thanks.