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.
onhashchangeevent which hooks into
http://mywebsite.com/#!page1and send a ajax request off to some custom server-side code made to handle that ajax
http://mywebsite.com?_escaped_fragment_=page1is exactly the same of what we would have traditionally expected to be at
http://mywebsite.com/page1and have it accessible via search Engines
These issues are unavoidable if hashes are used.
http://mywebsite.com/page1#/page2the browser starts on
http://mywebsite.com/page1then does the ajax request and loads in
page2- causing two loads for the initial access instead of just one.
These issues are generally coupled with the use of hashes despite them just being a result of over-engineering and can be simply avoided.
http://twitter.com/baluptonforces a redirect to
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.
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:
What does this code do?
http://mywebsite.com/page1is 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.
http://mywebsite.com/#/page1is accessed it will perform an ajax request to our traditional url
http://mywebsite.com/page1fetch 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!
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:
What we do here is if
http://mywebsite.com/page1 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.
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:
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 a 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.
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.
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 :)
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 :)
Any comments, concerns, feedback, want to get in touch? Here are my details.
Sharing is by far the most valuable exercise you can do! Here are some pre-made tweets for you:
Nice summary of state handling via the URL in modern web apps: https://github.com/browserstate/history.js/wiki/Intelligent-State-Handling (via @balupton)
The current state of the HTML5 History API across the different browsers, and why we need History.js - http://bit.ly/nUcW3L
Copyright 2011 Benjamin Arthur Lupton Licensed under the Attribution-ShareAlike 3.0 Australia (CC BY-SA 3.0)