PJAX is a jQuery extension that combines AJAX requests to HTML5 pushState. What this means is an AJAX website with a working back button. Think Twitter without ugly hash bangs (#!) in the URL**.
What AJAX, and by extension PJAX, does is allow for partial page reloads. The result is a much faster page load time – the browser doesn’t have to re-render the entire page. You can use PJAX out of the box in your WordPress theme, just specify which container is to be extracted from a full page request and the PJAX library extracts it and pops it into the page where you want it.
We’re going to go a bit beyond that here. Instead, we’ll hijack WordPress’ template system and include partial pages instead of complete pages – less stuff to send on every page load.
For this example, we’re going to create a Twenty Eleven Child Theme.
Using PJAX is very easy, and you can take a look at the docs to learn more. The JavaScript side of this tutorial is pretty unexciting.
jQuery(document).ready(function() {
jQuery('a').pjax('#main');
jQuery('#main').bind('pjax:end', function(){
if(typeof(pjaxy_page_info) != 'undefined') {
jQuery('body').attr('class', pjaxy_page_info.body_class);
jQuery('head title').html(pjaxy_page_info.page_title);
if(pjaxy_page_info.header_img) {
jQuery('header#branding > a img').attr('src', pjaxy_page_info.header_img)
.attr('width', pjaxy_page_info.header_width)
.attr('height', pjaxy_page_info.header_height);
}
}
jQuery('li.menu-item').removeClass('current-menu-item');
});
});
The first line inside our document ready
call turns on the PJAX functionality. The rest of it is to replace various page elements outside the #main
div with updated versions. We’ll need to update the <title>
to let folks know they’ve moved to a new page. We also need to update the body class to make sure everything renders properly. I’ll explay the pjaxy_page_info JS object and how it gets generated a bit later on.
Detecting PJAX Requsts
The PJAX library adds a X-PJAX header, which can use to detect whether a request was made via PJAX.
<?php
function is_pjax()
{
if( isset( $_SERVER['HTTP_X_PJAX'] ) && strtolower( $_SERVER['HTTP_X_PJAX'] ) == 'true' ) {
return true;
}
return false;
}
Hijacking Template Includes
Next up, if we detect a PJAX request we want to ditch the normal templates and include a stripped down version. To do this we can hook into the various {$template_type}_template
filters. If it’s the home page, for instance, the filter is home_template
. Singular page templates are single_template
.
Here’s an example.
<?php
add_filter( 'single_template', 'pjaxy_singular_template' );
function pjaxy_singular_template( $template )
{
if( is_pjax() ) {
$post = get_queried_object();
$temps = array( "single-{$post->post_type}.php" );
$temps[] = 'single.php';
if( $t = pjaxy_find_template( $temps ) ) {
$template = $t;
}
}
return $template;
}
This is a blantant rip off of the WP core get_single_template
. As are the rest of the template filter functions in this file.
The difference is the pjaxy_locate_template
function. Which is going to look for the correct theme file in the current child theme. If it doesn’t file one, it will fall back to the parent theme. We’re looking for our templates inside the pjax-templates
folder of the child or parent theme.
<?php
function pjaxy_find_template( $templates )
{
$located = '';
foreach( (array) $templates as $t ) {
if( ! $t ) {
continue;
} elseif( file_exists( STYLESHEETPATH . '/pjax-templates/' . $t ) ) {
$located = STYLESHEETPATH . '/pjax-templates/' . $t;
break;
} elseif( file_exists( TEMPLATEPATH . '/pjax-templates/' . $t ) ) {
$located = TEMPLATEPATH . '/pjax-templates/' . $t;
break;
}
}
return $located;
}
This is, of course, ripped off from the WP core’s locate_template
function.
Creating Template Files
PJAX templates will be a bit different than your normal WordPress template files. Basically what I did for the pjaxy theme was copy everything from the corresponding normal template files except the calls to get_header
and the like. We don’t need all that extra stuff – just the content that will get plugged into the #main
div.
You can view some examples on Github.
Changing Elements Outside the Main Div
When a PJAX call completes it fires off the pjax:end
event. You can use jQuery to listen in for this event (via jQuery('#main').bind('pjax:end')
). With that done, it’s a simple matter of a bit of JavaScript to replace body classes and such.
At the top of every template file is a call to the function get_pjaxy_page_info
which is just a wrapper around a require_once
statement.
<?php
function get_pjaxy_page_info()
{
require_once( PJAXY_PATH . 'inc/page-info.php' );
}
If you want to use any of the above code in your theme, the page-info.php
file will probably get changed. The only thing we’re doing here is creating a JavaScript object with the stuff we need to replace outside the #main
div.
<?php
$header_src = get_header_image();
$header_width = HEADER_IMAGE_WIDTH;
$header_height = HEADER_IMAGE_HEIGHT;
if( is_singular() ) {
$post = get_queried_object();
if( $thumb_id = get_post_thumbnail_id( $post->ID ) ) {
$header_image = wp_get_attachment_image_src( $thumb_id, array( HEADER_IMAGE_WIDTH, HEADER_IMAGE_WIDTH ) );
if( $header_image[1] >= HEADER_IMAGE_WIDTH ) {
$header_src = $header_image[0];
$header_width = $header_image[1];
$header_height = $header_image[2];
}
}
}
?>
<script type="text/javascript">
var pjaxy_page_info = {
body_class: "<?php echo esc_js( join( ' ', get_body_class( 'pjax-loaded' ) ) ); ?>",
page_title: "<?php echo esc_js( wp_title( '', false ) ); ?>",
header_img: "<?php echo esc_js( $header_src ); ?>",
header_height: "<?php echo esc_js( $header_height ); ?>",
header_width: "<?php echo esc_js( $header_width ); ?>"
}
</script>
What all this boils down to is figuring out to change how WordPress acts when a PJAX request is received. It’s a proof of concept, but it certainly speeds up page load times on the client side – take a look at the demo. Worth a look if you’re building a new theme!
** Twitter is moving away from this at the time of writing.