Thoughts on PJAX

Before starting work on Kamishibai, I was working with PJAX. PJAX was a nice and clean idea for implementing the HTML5 History API. The promises of PJAX was speeding up of web loading times by virtue of AJAX partial page loading, while at the same time, maintaining standard URLs and being web crawler friendly.

There were several problems with PJAX that made it difficult to work with and suboptimal for a flaky network environment.

For situations where the AJAX container changed between pages, PJAX simply reverted to re-issuing a full page load. This happened often when going backwards. For example, if we were moving forwards towards a page that changed in only a small fragment. Then we would set the AJAX container to the small fragment and this is all that is entered into the History state object. However, when we move back, then that would mean that we have to redraw more than the just the small fragment; we have to redraw the stuff surrounding it. PJAX simply balked in these situations and reverted to a full page load.

It’s great that with PJAX, there is always a way to simply reload the page and get a regular response that will always work. The issue is that it reverts to this too easily.

Also, this means that PJAX will send out two different types of requests depending on the situation it finds itself in. This makes it complicated and wasteful if we want to store the responses in localStorage. For example, if we do a reload of a deep-linked page, we would receive the whole HTML page. The question is, do we want to store all of it in localStorage under the deep-URL? When we request the same URL via PJAX, should we actually use the HTML body in localStorage? What if the case is reversed and we have an HTML fragment in localStorage, but the browser has requested a full reload. What should we show?

I was also unhappy with the way PJAX tended to waste the History memory. PJAX stores a copy of each AJAX body in the History state object. The problem is that this information can only be retrieved when going back. We can’t use this cache to load pages going forward and we have no way to check whether a certain fragment has already been stored. Given the low speed and reliability of mobile networks and the limited RAM of mobile devices, it doesn’t make sense to be so conservative with the use of precious memory.

Another factor is the lack of support of History API in the Android 4.0 and 4.1 stock browsers. Of course there is Chrome on these platforms, but it doesn’t make sense. The lack of History API on Internet Explorer up to 9.0 doesn’t help either, since it means that half of the browsers in the conference room will be gobbling up unnecessary amounts of bandwidth.

Since PJAX takes a one-page-at-a-time approach to the idea of URL resources, it is very compatible with a pure HTML document model, but is not really efficient for cases where bandwidth is limited. The issue that I had is with the use of the application cache manifest. The application cache manifest work with a full HTML document as the smallest unit (fragment). It does not work with Ajax, and requires a full HTML page reload. Therefore it doesn’t work at all with PJAX unless we cache all entry URLs. On the other hand, if we take a hash-based navigation approach, we will only need a single resource URL in the cache manifest. This URL will return the HTML header and body responsible for the “chrome” of all pages. To put it another way, if we wish to use the cache manifest with PJAX, the only way to do it would be to have entries for every single page that could be an entry-page. This would be extremely wasteful, and even if we could do this, we wouldn’t be able to navigate between these pages with AJAX since AJAX wouldn’t access the cached pages.

In summary, we decided against PJAX due to issues in the current implementation. It is also very likely that we will not use PJAX even if the library improves and browser support becomes more ubiquitous. This is because we feel that PJAX cannot use the cache manifest efficiently.

Leave a Reply

Your email address will not be published. Required fields are marked *