Jump to Table of Contents

Pjax

Pjax is a technique that allows you to progressively enhance normal links on a page so that clicks result in the linked content being loaded via Ajax and the URL being updated using HTML5 pushState, avoiding a full page load. In browsers that don't support pushState or that have JavaScript disabled, link clicks will result in a normal full page load. The Pjax Utility makes it easy to add this functionality to existing pages.

Getting Started

To include the source files for Pjax and its dependencies, first load the YUI seed file if you haven't already loaded it.

<script src="http://yui.yahooapis.com/3.8.0/build/yui/yui-min.js"></script>

Next, create a new YUI instance for your application and populate it with the modules you need by specifying them as arguments to the YUI().use() method. YUI will automatically load any dependencies required by the modules you specify.

<script>
// Create a new YUI instance and populate it with the required modules.
YUI().use('pjax', function (Y) {
    // Pjax is available and ready for use. Add implementation
    // code here.
});
</script>

For more information on creating YUI instances and on the use() method, see the documentation for the YUI Global Object.

Using Pjax

Quick Start

It's easy to add pjax functionality to any page with just a few lines of code. You don't even need any special server-side logic. Here's a simple example:

<h1>Links</h1>

<ol>
    <li><a href="page1.html" class="yui3-pjax">Page One</a></li>
    <li><a href="page2.html" class="yui3-pjax">Page Two</a></li>
    <li><a href="page3.html" class="yui3-pjax">Page Three</a></li>
</ol>

<div id="content"></div>

<script src="http://yui.yahooapis.com/3.8.0/build/yui/yui-min.js"></script>
<script>
YUI().use('pjax', function (Y) {
    new Y.Pjax({container: '#content'});
});
</script>

This creates a new Pjax instance that, by default, listens for clicks on any link on the page that has a yui3-pjax class. When a yui3-pjax link is clicked, its URL will be loaded via Ajax and the loaded content will be inserted into the #content div, replacing its existing contents. If the loaded page includes an HTML <title> element, the current page's title will also be replaced with the new title.

One important thing to note is that since Pjax uses XMLHttpRequest under the hood, it can only load URLs from the same origin as the current page. This means the link URL must share the same protocol, port, and host as the current page. Pjax will ignore URLs it can't load, resulting in a full page load for those URLs.

Instantiating Pjax

There are two ways to instantiate the Pjax Utility: you can load the pjax-plugin module and use the Y.Plugin.Pjax Node plugin, or you can load the pjax module and create a standalone instance of the Y.Pjax class.

Both instantiation methods provide the same core functionality; they only differ in how they're instantiated. Feel free to use whichever one you prefer.

As a Plugin

To instantiate Pjax as a plugin, load the pjax-plugin module and then plug the Y.Plugin.Pjax class into a Node instance. The node will be used as the container element, and content loaded via Pjax will replace the contents of the node.

YUI().use('pjax-plugin', function (Y) {
    Y.one('#content').plug(Y.Plugin.Pjax);
});

You may optionally pass configuration attributes as the second argument to plug():

Y.one('#content').plug(Y.Plugin.Pjax, {
    linkSelector: 'a.pjax',
    timeout     : 10000
});

As a Class

To instantiate Pjax as a class, load the pjax module and then create a new instance of Y.Pjax, specifying the node you want to use as a content container. You may optionally provide additional configuration attributes as well.

YUI().use('pjax', function (Y) {
    new Y.Pjax({container: '#content'});
});

Configuring Pjax

All configuration attributes are optional, although you'll usually want to at least specify a container node if you aren't using the Pjax plugin.

Config Attributes

Attribute Type Default Description
addPjaxParam Boolean true

If true, a "pjax=1" query parameter will be appended to all URLs requested via Pjax.

Browsers ignore HTTP request headers when caching content, so if the same URL is used to request a partial Pjax page and a full page, the browser will cache them under the same key and may later load the cached partial page when the user actually requests a full page (or vice versa).

To prevent this, we can add a bogus query parameter to the URL so that Pjax URLs will always be cached separately from non-Pjax URLs.

container Node null

Node into which content should be inserted when a page is loaded via Pjax. This node's existing contents will be removed to make way for the new content.

If not set, loaded content will not be automatically inserted into the page.

contentSelector String null

CSS selector used to extract a specific portion of the content of a page loaded via Pjax.

For example, if you wanted to load the page example.html but only use the content within an element with the id "pjax-content", you'd set contentSelector to "#pjax-content".

If not set, the entire page will be used.

linkSelector String "a.yui3-pjax"

CSS selector string used to filter link click events so that only the links which match it will have the enhanced navigation behavior of Pjax applied.

When a link is clicked and that link matches this selector, Pjax will attempt to load it via Ajax. If HTML5 history is not supported, or if the link was middle-clicked, right-clicked, or clicked while a modifier key was pressed, the link click will be handled by the browser just like any old link.

navigateOnHash Boolean false

Whether navigating to a hash-fragment identifier on the current page should be enhanced and cause the navigate event to fire.

By default Pjax allows the browser to perform its default action when a user is navigating within a page by clicking in-page links (e.g. <a href="#top">Top of page</a>) and does not attempt to interfere or enhance in-page navigation.

scrollToTop Boolean true

Whether the page should be scrolled to the top after navigating to a URL.

When the user clicks the browser's back button, the previous scroll position will be maintained.

timeout Number 30000

Time in milliseconds after which an Ajax request should time out. When a timeout occurs, the error event will be fired.

titleSelector String "title"

CSS selector used to extract a page title from the content of a page loaded via Pjax.

By default this is set to extract the title from the <title> element, but you could customize it to extract the title from an <h1>, or from any other element, if that's more appropriate for the content you're loading.

Custom Selectors

Pjax's contentSelector and titleSelector config attributes allow you to customize how content and page titles are extracted from loaded pages, while the linkSelector attribute lets you customize which links on the page are loaded via Pjax.

contentSelector

By default, contentSelector is null, meaning that the entire contents of any loaded page will be inserted into the container node. This could be bad if the page includes a header and footer that shouldn't be displayed again. Specify a custom contentSelector to display only a portion of the loaded page.

Let's say we have an HTML page that looks like this:

<!DOCTYPE html>
<title>The Surprising Adventures of Sir Digby Chicken Caesar - Episode 1</title>
<body>
    <div class="header">
        <img src="header.jpg" alt="Sir Digby and his companion Ginger">
        <h1>The Surprising Adventures of Sir Digby Chicken Caesar</h1>
    </div>

    <div class="episode-content">
        <h2>Episode One</h2>

        <p>
        On a lonely planet spinning its way toward damnation amid the fear and
        despair of a broken human race, who is left to fight for all that is
        good and pure and gets you smashed for under a fiver? Yes, it's the
        surprising adventures of me, Sir Digby Chicken Caesar!
        </p>
    </div>

    <div class="footer">
        <p>
        Copyright (c) 2012 Digby Enterprises.
        </p>
    </div>
</body>

Since the header and footer are persistent across the site, we only want to display the content portion when this page is loaded via Pjax. All we need to do is set the contentSelector attribute to the appropriate CSS selector:

// Extract the contents of the '.episode-content' div from loaded pages and
// discard the rest.
new Y.Pjax({
    container      : '#content',
    contentSelector: '.episode-content'
});
linkSelector

The linkSelector attribute allows you to customize which links Pjax will handle. By default, linkSelector is set to "a.yui3-pjax", which means that any <a> element with the class name "yui3-pjax" will be handled by Pjax. You could customize this to change the class name, or to limit Pjax to links inside a certain container, or anything else you can do with a CSS selector.

titleSelector

The titleSelector attribute allows you to customize how Pjax extracts a page title from loaded pages. By default, it's set to the selector string "title", which means it will extract the contents of the first <title> element on the loaded page. If desired, you could customize this selector to extract the title from an <h1> element, or any other element, or set it to null to prevent a title from being extracted.

Pjax Events

Event Description Payload
error

Fired when an error occurs while attempting to load a URL.

content (Object)

Content extracted from the response, if any. This is an object with the following properties:

node (Node)
A Y.Node instance for a document fragment containing the extracted HTML content.
title (String)
The title of the HTML page, if any, extracted using the titleSelector attribute. If titleSelector is not set or if a title could not be found, this property will be undefined.
responseText (String)
Raw Ajax response text.
status (Number)
HTTP status code for the Ajax response.
url (String)
The absolute URL that failed to load.
load

Fired when a URL is successfully loaded.

content (Object)

Content extracted from the response, if any. This is an object with the following properties:

node (Node)
A Y.Node instance for a document fragment containing the extracted HTML content.
title (String)
The title of the HTML page, if any, extracted using the titleSelector attribute. If titleSelector is not set or if a title could not be found, this property will be undefined.
responseText (String)
Raw Ajax response text.
status (Number)
HTTP status code for the Ajax response.
url (String)
The absolute URL that was loaded.
navigate

Fired when navigating to a URL via Pjax. This is a useful event to listen to if you want to add a visual loading indicator while content is loading.

force (Boolean)
true if enhanced navigation should occur even in browsers that don't support HTML5 history.
originEvent (Event)
The event that triggered navigation. Usually this will be a DOM click event.
replace (Boolean)
true if the current history entry will be replaced instead of a new entry being created.
url (String)
The URL being navigated to.

Customizing Pjax

There are many situations where more advanced uses of the Pjax Utility need to customize its default behavior. Adding analytics, changing how content is added to the page, performing some operation before or after the standard behavior, etc. are all cases where Pjax needs to be customized. Fortunately, the hooks are in place to aid customization.

Enhancing Navigation with Pjax Events

One way to enhance the experience for people moving between "pages" of your site or app, is to add a loading indicator while the contents of the page are being loaded from the server via the Pjax utility. This may be especially important for pages which have dynamic content and might take a little while to generate on the server.

The follow provides a basic example of how to add a loading indicator using the Pjax's events:

var pjax = new Y.Pjax({container: '#content'});

// Listen for the `navigate` event and add the "loading" CSS class. The styles
// for the this CSS class can add a loading spinner or dim the container node.
pjax.on('navigate', function (e) {
    this.get('container').addClass('loading');
});

// Listen for either the `load` or `error` events and remove the "loading" CSS
// class from the container node.
pjax.on(['error', 'load'], function (e) {
    this.get('container').removeClass('loading');
});

If you're looking to page transitions to your application, you might want to look at the App component which builds on the Pjax utility and supports transitions.

Using Specific Routes

By default, any link within a pjax's container—when clicked—will be handled via pjax and the content at the link's URL will be loaded and dynamically inserted into the page without a full page load. Sometimes you might need to be more restrictive, you may want only certain portions of your site to be pjax-ified.

The Pjax utility is build on the Router component, this mean that a pjax instance is a router. By specifying a value for the routes attribute, you can customize which URL paths you want the Pjax utility to handle. The Pjax utility also provides Y.Pjax.defaultRoute, a stack of route middleware which forms the default Pjax route handler.

The following specifies the routes which the Pjax utility should handle, and uses Pjax's default route handler to load the content from the server and dynamically insert it into the page:

new Y.Pjax({
    container: '#content',

    // This tells the Pjax utility to only handle the About Us and Contact Us
    // sections of our site by using the default route handler.
    routes: [
        {path: '/about',   callbacks: Y.Pjax.defaultRoute},
        {path: '/contact', callbacks: Y.Pjax.defaultRoute}
    ]
});

See the Routing section of Router's user guide for more information.

Adding Analytics Support

One issue people will quickly run into when using the Pjax utility is needing to report page views to their web analytics system. Even though Pjax removes unnecessary full page loads, we still want to track page views for the content it loads.

There are several hooks available for tracking page views for content which is loaded via pjax. You could listen to the load and error events, or specify the routes by providing an additional route-specific middleware, but there's a simpler way… Most likely you want your web analytics to track page views for all pages loaded through all pjax instances on the page. The most straight-forward way is to push() additional middleware on to Pjax's defaultRoute.

The following shows how you can hook into Google Analytics, and track page views for all pages that are handled via the Pjax utility:

// This assumes you already have Google Analytics on the page.

// Define the a middleware function which will track page views with GA.
function trackPageview(req, res, next) {
    if (Y.config.win._gaq) {
        _gaq.push(['_trackPageview', res.url]);
    }

    next();
}

// Push our `trackPageview()` middleware onto Pjax's `defaultRoute`.
Y.Pjax.defaultRoute.push(trackPageview);

// Now any pjax instance we create on this page will automatically have
// page views tracked via Google Analytics.
new Y.Pjax({container: '#content'});

See the Chaining Routes and Middleware section of Router's user guide for more information.

Optimizing Server Responses for Pjax

While the Pjax Utility is capable of extracting and displaying small portions of loaded pages, it's much more efficient to avoid sending unnecessary content in the first place. With a little extra work on the server side, you can have the server recognize Pjax requests and send back only the relevant portion of the requested page.

X-PJAX HTTP Header

When the Pjax Utility makes an Ajax request to the server, it adds a special X-PJAX HTTP header to the request. You can check for the presence of this header on the server when handling the request to determine whether you should respond with a full page or a partial page.

A complete HTTP request generated by the Pjax Utility looks something like this:

GET /example.html?pjax=1 HTTP/1.1
Host: example.com
X-Requested-With: XMLHttpRequest
X-PJAX: true
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.75 Safari/535.7
Accept: */*
Referer: http://example.com/index.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

pjax Query Parameter

When the addPjaxParam config attribute is true (the default), the Pjax Utility will add a pjax=1 query parameter to the end of all URLs it requests. This serves two purposes:

  1. It provides an alternative to the X-PJAX HTTP header for determining whether a request was generated by the Pjax Utility.
  2. It ensures that the browser caches Pjax responses separately from full page responses.

Known Issues

  • Pjax's partial page loads only work in browsers that support HTML5 history (most modern browsers do). In older browsers such as IE6 through IE9, pjax-enabled links will result in full page loads. This is by design, since it allows you to take advantage of the functionality supported by modern browsers while providing a graceful fallback for older browsers.