-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Intelligent State Handling
Everyone seems to use the hashbang (#!) in URLs now... despite google recommending it - hashbangs are a ridiculously bad solution.
- Google is the only search engine which supports it.
- JS-Disabled users who access a hashed url won't have the right page
- There becomes with two urls for the same content - e.g.
http://twitter.com/balupton
andhttp://twitter.com/#!/balupton
- The url's gets polluted if we did not start on the home page - e.g.
http://www.facebook.com/balupton#!/balupton?sk=info
- It is likely that the effort won't be put in to make the site accessible to non-javascript users and other search engines - e.g. forcing people to use
http://twitter.com/#!/balupton
instead ofhttp://twitter.com/balupton
- It is likely that custom controller actions will be coded to handle the ajax requests specifically - rather than AJAXing the original urls and returning just the appropriate content (the template and any data, not the full page).
Problems #1 and #5 are solved by coding your website normally (without any server-side ajax support), hooking into the hashchange, and querying the non-hashed version of the new hashed url:
var $content = $('#content');
// hook into http://mysite.com/#/page
$(window).bind('hashchange',function(){
// ajax request http://mysite.com/page
var url = document.location.protocol+'//'+(document.location.hostname||document.location.host)+'/'+document.location.hash;
$.get(url,function(data){
// find content in the page's html, and apply it to our current page's content
$content.html($(data).find('#content'));
}
}
The solution above is a great improvement, though it still suffers from the other problems. The problem of #6 would be the next logical step, as the current solution causes a lot of overhead (we just need the updated content, not the entire page), so let's solve that now with the following server-side code:
<?php
// Our Page Action
public function pageAction ( ) {
// Prepare our variables for our view
// ...
// Handle our view
return $this->render('page.html');
}
// Render Helper
public function render ( $template ) {
// Get the full template path
$template_path = ...
// Render the template
$template_html = file_get_contents($template_path);
// Check for the XHR header
if ( IS_XHR ) {
// We are a AJAX Request, return just the template
$this->sendJson({'content':$template_html});
}
else {
// Wrap the Template HTML with the Layout and proceed as normal
// ...
}
// Done
}
In fact, jQuery Ajaxy has supported these solutions for problems #1, #5 and #6 out of the box since July 2008. There is also a Zend Framework Action Helper to make solving problem #6 easier.
Though, this still suffers from the other problems. This is where the HTML5 History API comes to the rescue, as without that it is not possible to solving those problems. The HTML5 History API allows users to modify the URL directly, attach data and titles to it, without changing the page! Yay.
That's great, though each HTML5 Browser (Safari, Chrome and Firefox 4) all support it differently, so if you just use the native API available to you, you're going to come across a series of bugs. History.js has been made to provide a cross-compatible solution for the HTML5 browsers, as well as supporting HTML4 browsers by a hash (#) fallback (optional as of v1.5). We do not use a hashbang (#!) fallback as it would be useless as our website is already indexable by search engines.
So what would the ultimate solution look like that solves all these problems?
var $content = $('#content');
// hook into http://mysite.com/#/page
$(window).bind('statechange',function(){
// ajax request http://mysite.com/page
$.get(History.getState().url,function(data){
// find content in the page's html, and apply it to our current page's content
$content.html($(data).find('#content'));
}
}
And bang you have just solved 5 out of 6 of all our problems. Problem 6 requires a server side effort - plugins are currently being made for the every darn CMS and Framework.
Okay, so that sounds too good to be true. What do we need to be aware of?
- If you choose to support HTML4 browsers by using a hash fallback using History.js the use case of "A HTML4 JS-enabled user shares a url with a JS-disabled user, the url does not work." - the only way to solve this is by just supporting states in HTML5 browsers and not HTML4 browsers - say not using the hash fallback at all.
- If you have multiple areas on your website that change (more than just one div called
#content
), or if you want to utilise multiple levels of ajaxable pages (e.g. pages as well as sub-pages) for further optimisation and a better experience then this requires some more server side work. Currently there is only the HTML4 jQuery Ajaxy + Zend Framework solution mentioned earlier. Plugins for HTML5 History.js + every darn CMS and Framework are coming soon.
Feel free to get in touch with me.
- Website: http://balupton.com
- Email: [email protected]
- Skype: balupton
- Twitter: balupton
Intelligent State Handling: Solutions for horrific hash/hashbang fallbacks and HTML5 History API (pushState) problems http://j.mp/etU7q6
Copyright 2011 Benjamin Arthur Lupton Licensed under the Attribution-ShareAlike 3.0 Australia (CC BY-SA 3.0)