PJAX Use Case On Intridea.com

Background

As a Rails developer I normally spend most of my time on backend development, implementing features and functionalities. I am really confident about my Rails skills for backend work but rarely have I felt any happiness doing frontend work before. But things have changed since working on the new responsive intridea.com. I started some interesting frontend work from this project and fell in love with many UI skills and JS tricks, especially Pjax which I want to talk more about in this post.

What is Pjax?

Pjax is such a fantastic tool. I love this formula which describes Pjax well: pushState + Ajax = Pjax. When you need to store your page state in URL as permanent links, Ajax cannot do it gracefully. Thus, Pjax is a good choice to only update specific parts of web page and allow you to generate permanent links at the same time. Personally, I also like to call Pjax Perfect Ajax.

Pjax Magic in Rails 3.2

Let me show you how we use Pjax to speed up page loading for the current intridea.com website. Firstly, we use a rack middleware rack_pjax which will simply filter the page to respond to pjax-requests accordingly. Ok, add the gem to Gemfile as first step:

 gem 'rack_pjax' 

Secondly, we should include this rack application into our Rails stack. This is easy too, just add this line in your config/application.rb file to config the middleware:

 config.middleware.use Rack::Pjax 

Thirdly, we install the Pjax jquery plugin to assets/javascripts folder. You can download the Pjax source from this link. As with any other javascript plugin, be sure to include the file in application’s JavaScript manifest file as below:

 //= require jquery  //= require jquery_ujs  //= require chosen.jquery.min  //= require jquery.pjax  //= .... other js libs 

OK, by doing the above three steps of installation and configuration we now have Pjax plugged into our application. It's time to enable the Pjax RULE for our current website. Basically, we want to add a 'data-pjax-container'-attribute to the container in which the HTML content will be updated by the Pjax response. The Pjax data container can be put in any layout or view file in our Rails application. That sounds cool, right? In our case, we only place a Pjax data container in layout as below:

 

Wait, it's not finished yet. Now we enable the Pjax magic for the application. We enabled all links as "Pjax" triggers as below for example:

 $(document).ready(function() {    $('a').pjax('[data-pjax-container]', {timeout: 3000});  }); 

It means, every link on the web page will trigger a Pjax request to the server, then the Pjax data container part will be updated by the Pjax response. Here we set timeout as 3000ms; you can set it higher if you use a custom error handler. Besides timeout, there are a bunch of other options for a Pjax function; they are almost the same as jQuery's $.ajax(), but there are still some additional options, you can take a detailed look at Pjax's doc.

Attentions

We have some javascript code which are binding to some Dom elements in that Pjax data container. For instance, we want to validate our contact form via Javascript, but a Pjax based page reloading will prevent the Javascript validator from working. That's because we only initialize the validator when Document is ready, but a Pjax reloading will not reload the whole document, which means we have to recall the validator again after the Pjax is done. Yeah, Pjax indeed fires two basic events while doing Pjax on your data container: pjax:start and pjax:end. To solve the above javascript validation issue, we need to call that function in Pjax:end callback as well.

 $(document).ready(function() {    var ApplicationJS = com.intridea.ApplicationJS;     $('a').pjax('[data-pjax-container]', {timeout: 8000});    ApplicationJS.validate_contact_form('#contact_form');     $(document).on('pjax:end', function(){      ApplicationJS.validate_contact_form('#contact_form');    });  }); 

Similarly, if you want to add a loading indicator upon Pjaxing, then you might need to do something like this:

   $(document).on('pjax:start', function(){       // this will show an indicator on the <li> tag in navigation.       ApplicationJS.navSpinner('nav li.active');    }); 

Finally, notice that Pjax only works with browsers that support the history.pushState API. Here is a table to show you all related information about popular browsers. When Pjax is not supported in some browser, $('a').pjax('[data-pjax-container]' calls will do nothing but work as normal links. So don't worry about any regarding mess ups.

Have fun playing with Pjax, and please share your feedback and your own use cases with us on Twitter!

Resources