Skip to content

Intelligent State Handling

Facundo Galán edited this page Nov 21, 2016 · 35 revisions

Hashbangs (#!), hashes (#) and even the HTML5 History API (pushState, popState) all have issues. This article will go through the issues with each one, their use cases, then provide the evolution of their solutions. At the end with little bit of educated simplicity you'll be able to achieve better results; in terms of a better experience for your users as well as better compatibility, accessibility and maintainability in your solutions.

The Problems

Issues Coupled with the Hashbang

  1. Your website will require tedious routing, mapping and escaping on your applications side which break the traditional web architecture 1, 2:
    1. Have the traditional url that we are use to redirect to!page1
    2. Code an onhashchange event which hooks into!page1 and send an ajax request off to some custom server-side code made to handle that ajax
    3. Ensure that is exactly the same of what we would have traditionally expected to be at and have it accessible via search Engines
  2. Your website will no longer work for js-disabled users [Note: This depends on your solution for #1.1 above. A client-side javascript redirect, while more costly for the average request, would support rendering the expected page for js-disabled visitors.] and is no longer crawlable by search engines other than Google (a sitemap will have to be provided to them).

Issues Coupled with Hashes

These issues are unavoidable if hashes are used.

  1. There are now two urls for the same content
    1. and!/balupton
    2. and
  2. URLs get polluted if we did not start on the home page
  3. If a user shares a hashed url with a js-disabled user - the user will not get the right page.
  4. Performance and experience issues when a hashed url is loaded.
    1. When a user accesses the browser starts on then does the ajax request and loads in page2 - causing two loads for the initial access instead of just one.
    2. This is an experience issue as it is annoying for the user as they are either stuck on a "loading" page, or they start scrolling the initial page only for it to disappear and change to another page.

Issues Coupled with Over-Engineering

These issues are generally coupled with the use of hashes despite them just being a result of over-engineering and can be simply avoided.

  1. Using the hashbang and inheriting its problems.
  2. Having no support for the traditional url at all, users are forced to use the hashed url; disabling the site for non-js users and search engines.
    1. forces a redirect to!/balupton
  3. Coding custom and separate AJAX controller actions the client and server side breaking DRY and graceful best practices

The Solution


Some pretty bold statements follow; especially as the statements challenge the ways things have been done for a very long long time. So lets put our egos aside together and show some humility to be open to learning new ways of doing things. Feeling open to learning some awesome new ways of doing things? Great. Let's continue.

Why The HashBang is Unnecessary

There is absolutely no need for the hashbang; it is credited to over-engineering on google's behalf. The following snippet of code is all that your traditional website needs to use hashes and provide rich ajax experiences, support search engines, js-disabled users and even google analytics:

View the code snippet

What does this code do?

  1. When is accessed it works just as it would traditionally - so search engines and js-disabled users are naturally supported. This is without any tedious server-side routing, mapping or escaping. You've coded your website just as you would normally.
  2. When is accessed it will perform an ajax request to our traditional url fetch the HTML of that page, and load in the page's content into our existing page.

So already we have a crawlable ajax solution accessible by search engines and js-disabled users without any server-side code. Take that google!

AJAX Response Optimisation

So the above is great, but it still fetches the entire HTML of each page it does a AJAX request for - when really we just need the content of the page we want (the template without the layout). Let's utilise the following server side code in our page action:

View the code snippet

What we do here is if is requested normally treat it just as normal rendering it with the layout, if it is requested via AJAX then return just the rendered template in a JSON response. This can easily be extended so we can send JSON data variables along with the rendered content. In fact jQuery Ajaxy has supported these solutions out of the box since July 2008, as well as having a Zend Framework Action Helper to make these server-side optimisations easier and more powerful (supporting sub-pages/sub-templates, data attaching, caching, etc).

So right now we have a crawlable ajax solution which is also incredibly optimised. Though it still suffers from the problems coupled with hashes - which are unavoidable as long we still use hashes.

The HTML5 History API - Our Saviour

Recently the HTML5 History API came out which is literally our saviour - it solves the issues coupled with hashes once and for all. The HTML5 History API allows users to modify the URL directly, attach data and titles to it, all without changing the page! Yay! So let's look at what our updated code example will look like:

View the code snippet

Though so far all the HTML5 Browsers handle the HTML5 History API a little bit differently; a pessimist could view this as a blocker and a call for defeat, though an optimist could come along and create a project called History.js which provides a cross-compatible experience between HTML5 and optionally HTML4 browsers fixing all the bugs and issues in the browsers. In fact, the code above already works perfectly with History.js - so bye bye learning curve you're all set to go already.

Supporting HTML4 Browsers - The Ultimate Decision

Okay okay... So what about HTML4 browsers, wouldn't they miss out on all this awesome HTML5 History API awesomeness? Well no and yes - it depends. This is where you need to make a serious decision and a lot of consideration. The question you have to ask yourself is - what is more important to me; supporting the rich web 2.0 ajax experience in HTML5 and HTML4 browsers while incurring the issues that are coupled with hashes when the site is accessed by a HTML4 user, or not incurring those issues by not supporting a rich web 2.0 ajax experience in HTML4 browsers. That is a decision that only you can make based on your websites use cases and audience.

Pulling it All Together

Great, so all I need to do is use History.js, that code above and I've solved life? Yep. And if I want to support HTML4 browsers as the issues coupled with hashes aren't a biggie for me I can? Yep. And if I want to further optimise the AJAX responses I now know how? Yep. Well blimey that's awesome. Thanks :)

So What's Next?

History.js is as stable as it gets right now. The future is with improved documentation, education, and extensions and frameworks surrounding it - such as integrations with other content management systems. They are all under active development by Benjamin Lupton but he can always do with your help. If you'd like to help in any way (even if you're not sure how you can help out) then please do get in contact with him - his details are in the footer :)

The End

Any comments, concerns, feedback, want to get in touch? Here are my details.

Benjamin Lupton

Like it. Share it.

Sharing is by far the most valuable exercise you can do! Here are some pre-made tweets for you:

Further Reading


Copyright 2011 Benjamin Arthur Lupton Licensed under the Attribution-ShareAlike 3.0 Australia (CC BY-SA 3.0)

You can’t perform that action at this time.