-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
The State of the HTML5 History API
The HTML5 History API is hugely important as it finally allows us to create stateful web sites.
This works by pushing or replacing states via history.pushState(data,title,url)
and history.replaceState(data,title,url)
respectively.
-
data
: is a json object (optional) -
title
: is the title of the state (optional) -
url
: is the url of the state
Before the HTML5 History API we had to implement hacking workarounds such as transforming the traditional anchor in a url, into a stateful hash. Which caused and is still causing the web to break for search engines and javascript disabled users for websites which implement the hash workaround.
The HTML5 History API should provide us with a way forward, but it does still have it's problems which libraries such as History.js attempt to resolve.
The HTML5 History API adoption rate has been very quick by browser vendors. It is currently supported by the following browsers:
- Firefox 4+
- Google Chrome
- Safari 5+
- iOS 4
However, just because those browsers have implemented the HTML5 History API, the functionality of it is incoherent across all implementations. Which is a serious problem.
Firefox | Chrome | Safari | iOS | Opera | IE | History.js | |
---|---|---|---|---|---|---|---|
Minimum Working Version | 4 | 8 | 5 | 4.3 | None | None | 1.7 |
Supports onpopstate
|
4+ | 8+ | 5+ | 4.2+ | 11+ | None | onstatechange |
Supports onhashchange
|
3.6+ | 1+ | 5+ | 4.0+ | 8+ | None | onanchorchange |
Initial onpopstate
|
No | Yes | No | No | ? | ? | No |
Initial onhashchange
|
No | Yes | No | No | ? | ? | No |
Fires onpopstate with hash changes |
Yes | Yes | No | No | ? | ? | Only on state changes |
Fires onhashchange with hash changes |
Yes | Yes | No | No | ? | ? | Only on anchor changes |
States still set when busy report | Yes | Yes | Yes | Yes | No | ? | Yes |
States still fire when busy report | Yes | Yes | No | No | No | ? | Yes |
Can replace a hashed url report | Yes | Yes | No | No | ? | ? | Yes |
Back buttons work with new states | Yes | Yes | Yes | 4.3+ | ? | ? | Yes |
back/forward/bookmark/etc will trigger onpopstate
|
Yes | Yes | Yes | Yes | ? | ? | Yes |
pushState and replaceState will trigger onpopstate
|
No | No | No | No | ? | ? | Yes |
pushState and replaceState will set document.title
|
No | No | No | No | No | ? | Yes |
State data persists when navigated away and back | ? | One state only | ? | ? | ? | ? | Optional |
Transparent HTML4 Fallback | No | No | No | No | No | ? | Optional |
The HTML5 History API in it's most basic form is expected to be used like so
var myNewState = {
data: {
a: 1,
b: 2
},
title: 'My New State',
url: 'my-new-state.html'
};
history.pushState(myNewState.data, myNewState.title, myNewState.url);
window.onpopstate = function(event){
console.log(event.state); // will be our state data, so myNewState.data
}
Now say we want to ajaxify our entire website, we'll actually end up with a fair few lines of code as we need to:
-
Discover our internal links on our website, and upgrade them so when they are clicked it loads in their content via ajax instead.
- For this we need to detect our pages root url, so we can compare the links against it
-
Create a page loader function which is passed a url from our internal links handler, and our
onpopstate
. This will:-
Determine the absolute and relative urls from the passed url
-
Fade out our current page's content in anticipation of the new content
-
Send off an ajax request to the absolute url
-
Convert the response to a format jQuery will understand - as jQuery is only made to handle elements which go inside the body element, not elements made for the head element.
-
Extract the page's title and set
document.title
and the title element to it -
Scan for our new page's url in our menu items, and mark the menu link for our new page as active and the others as inactive
-
Finish the current content's fadeout animation
-
Update the current content with the new page's content
-
Scroll to the new current content so the user is directed to the right place - rather than them ending up looking at the footer or something instead of your page's content due to the height shift with the content change
-
Inform Google Analytics and other tracking software about the page change
-
Check if we were sent by
onpopstate
or an internal link, if we were sent by an internal link then push the new state with any data we want, the page's title, and the absolute url.
-
Click here to see the entire code snippet which does all of this
Now that we have our entire website ajaxified, it should be a time for celebration. However, due to the incoherence of browser implementations we're going to quickly start discovering some problems.
Look back at the table before and think about how each of the different behaviours will affect our code snippet. The most obvious ones are:
- In Safari, iOS and Opera as we are loading in our content via AJAX the browser will fail to set and/or create our state history entries, making navigating our ajax website impossible
- In Google Chrome the initial page load will cause our content to load in twice
Which can you come up with? If you're starting to think the HTML5 History API isn't as feasible as you first thought, if not entirely impossible to use natively - then you'd be right._
Given those problems, what are the solutions? There are two:
- Give up on the HTML5 History API and cry.
- Code a series of hacks and workarounds to try and provide a coherant interface for the HTML5 History API
Luckily, fortunately, blissfully, someone figured the HTML5 History API was too darn important to give up on and decided to pursue option 2. The result is Benjamin Lupton's History.js - so thankyou Benjamin!
Click here to see the earlier code snippet updated for History.js
Click here to try that updated code snippet with History.js on a real website
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 :)
Here are the author's details if you'd like to get in touch - he LOVES feedback! So please do get in touch.
- Email: [email protected]
- Skype: balupton
- Website
- Google+
Sharing is by far the most valuable exercise you can do! Here are some pre-made tweets for you:
- The current state of the HTML5 History API across the different browsers, and why we need History.js - http://bit.ly/nUcW3L
- The state of the HTML5 History API and why it isn't good enough - http://bit.ly/nUcW3L
- The History.js Readme: Your guide to History.js
- Intelligent State Handling: The evolution from hashes, to hashbangs to the HTML5 History API
Copyright 2011 Benjamin Arthur Lupton Licensed under the Attribution-ShareAlike 3.0 Australia (CC BY-SA 3.0)