This project is the successor of jQuery History, it aims to:
- Follow the HTML5 History/State APIs as much as possible
- Provide a cross-compatible experience for all HTML5 Browsers (they all implement the HTML5 History API a little bit differently causing different behaviours and sometimes bugs - History.js fixes this ensuring the experience is as expected / the same / great throughout the HTML5 browsers)
- Provide a backwards-compatible experience for all HTML4 Browsers using a hash-fallback (including continued support for the HTML5 History API's
data
,title
,pushState
andreplaceState
) with the option to remove HTML4 support if it is not right for your application - Provide a forwards-compatible experience for HTML4 States to HTML5 States (so if a hash-fallbacked url is accessed by a HTML5 browser it is naturally transformed into its non-hashed url equivalent)
- Provide support for as many javascript frameworks as possible via adapters; especially jQuery, MooTools and Prototype
Licensed under the New BSD License Copyright 2011 Benjamin Arthur Lupton
(function(window,undefined){
var History = window.History; // Note: We are using a capital H instead of a lower h
History.Adapter.bind(window,'statechange',function(){ // Note: We are using statechange instead of popstate
var State = History.getState(); // Note: We are using History.getState() instead of event.state
History.log(State.data, State.title, State.url);
});
History.pushState({state:1}, "State 1", "?state=1"); // logs {state:1}, "State 1", "?state=1"
History.pushState({state:2}, "State 2", "?state=2"); // logs {state:2}, "State 2", "?state=2"
History.replaceState({state:3}, "State 3", "?state=3"); // logs {state:3}, "State 3", "?state=3"
History.pushState(null, null, "?state=4"); // logs {}, '', "?state=4"
History.back(); // logs {state:3}, "State 3", "?state=3"
History.back(); // logs {state:1}, "State 1", "?state=1"
History.back(); // logs {}, "Home Page", "?"
History.go(2); // logs {state:3}, "State 3", "?state=3"
})(window);
- www.mysite.com
- www.mysite.com?state=1
- www.mysite.com?state=2
- www.mysite.com?state=3
- www.mysite.com?state=4
- www.mysite.com?state=3
- www.mysite.com?state=1
- www.mysite.com
- www.mysite.com?state=3
Note: These urls also work in HTML4 browsers and Search Engines. So no need for the hashbang (
#!
) fragment-identifier that google "recommends".
- www.mysite.com
- www.mysite.com#?state=1/uid=1
- www.mysite.com#?state=2/uid=2
- www.mysite.com#?state=3/uid=3
- www.mysite.com#?state=4
- www.mysite.com#?state=3/uid=3
- www.mysite.com#?state=1/uid=1
- www.mysite.com
- www.mysite.com#?state=3/uid=3
Note 1: These urls also work in HTML5 browsers - we use
replaceState
to transform these HTML4 states into their HTML5 equivalents so the user won't even notice :-)Note 2: These urls will be url-encoded in all HTML4 Browsers except Opera as it does not url-encode the urls. The url-encoding is necessary for these browsers as otherwise it won't work (the hashes won't actually apply). There is nothing we can do about this.
Note 3: Support for HTML4 browsers (this hash fallback) is optional - why supporting HTML4 browsers could be either good or bad based on my app's use cases
- UIDs are used when we utilise a
title
and/ordata
in our state. Adding a UID allows us to associate particular states with data and titles while keeping the urls as simple as possible (don't worry it's all tested, working and a lot smarter than I'm making it out to be). - If you aren't utilising
title
ordata
then we don't even include a UID (as there is no need for it) - as seen by State 4 above :-) - We also shrink the urls to make sure that the smallest url will be used. For instance we will adjust
http://www.mysite.com/#http://www.mysite.com/projects/History.js
to becomehttp://www.mysite.com/#/projects/History.js
automatically. (again tested, working, and smarter). - It works with domains, subdomains, subdirectories, whatever - doesn't matter where you put it. It's smart.
- Sure is, give it a download and navigate to the demo directory in your browser :-)
- If you are after something a bit more adventurous than a end-user demo, open up the tests directory in your browser and editor - it'll rock your world and show all the vast use cases that History.js supports. Note: you will have to [run a
git submodule init; git submodule update
and update thetests/.htaccess
for your path] prior to running the tests.
-
Download History.js and upload it to your webserver. Download links: tar.gz or zip
-
Include JSON2 for HTML4 Browsers Only (replace www.yourwebsite.com)
<script type="text/javascript"> if ( typeof JSON === 'undefined' ) { var url = 'http://www.yourwebsite.com/history.js/scripts/compressed/json2.js', scriptEl = document.createElement('script'); scriptEl.type = 'text/javascript'; scriptEl.src = url; document.body.appendChild(scriptEl); } </script>
-
Include the Adapter for your Framework:
-
<script type="text/javascript" src="http://www.yourwebsite.com/history.js/scripts/compressed/history.adapter.jquery.js"></script>
-
<script type="text/javascript" src="http://www.yourwebsite.com/history.js/scripts/compressed/history.adapter.mootools.js"></script>
-
<script type="text/javascript" src="http://www.yourwebsite.com/history.js/scripts/compressed/history.adapter.prototype.js"></script>
-
Would you like to support another framework? No problem! It's very easy to create adapters, and I'll be happy to include them or help out if you let me know :-)
-
-
Include History.js
<script type="text/javascript" src="http://www.yourwebsite.com/history.js/scripts/compressed/history.js"></script> <script type="text/javascript" src="http://www.yourwebsite.com/history.js/scripts/compressed/history.html4.js"></script>
Note: If you want to only support HTML5 Browsers and not HTML4 Browsers (so no hash fallback support) then just remove the
history.html4.js
include in step #4 file and the JSON2 include in step #2 - why supporting HTML4 browsers could be either good or bad based on my app's use cases
- For Email Updates:
- You can subscribe via the subscription form included in the demo page
- For Commit RSS/Atom Updates:
- You can subscribe via the GitHub Commit Atom Feed
- For GitHub News Feed Updates:
- You can click the "watch" button up the top right of History.js's GitHub Project Page
History.js is an actively developed, supported and maintained project. You can grab support via its GitHub Issue Tracker. Alternatively you can reach Benjamin Lupton (the core developer) via twitter, skype (balupton) or email ([email protected]).
If you'd love to give some support back and make a difference; here are a few great ways you can give back!
- Give it your honest rating on its jQuery Plugin Page
- If you have any feedback or suggestions let me know via its Issue Tracker - so that I can ensure you get the best experience!
- Spread the word via tweets, blogs, tumblr, whatever - the more people talking about it the better!
- Donate via the donation form at the bottom right of balupton.com - every cent truly does help!
- Make it easier for me to let you know about future releases and updates by subscribing via the signup form inside the demo page
- Watch it via clicking the "watch" button up the top of its Project Page
Thanks! every bit of help really does make a difference. Again thank you.
- Chrome 8,9
- Firefox 4
- Safari 5
- Safari iOS 4.2.1
- Opera 10,11
- Firefox 3
- IE 6,7,8
History.pushState(data,title,url)
Pushes a new state to the browser;data
can be null or an object,title
can be null or a string,url
must be a stringHistory.replaceState(data,title,url)
Replaces the existing state with a new state to the browser;data
can be null or an object,title
can be null or a string,url
must be a stringHistory.getState()
Gets the current state of the browser, returns an object withdata
,title
andurl
History.getHash()
Gets the current hash of the browserHistory.Adapter.bind(element,event,callback)
A framework independent event binder, you may either use this or your framework's native event binder.History.Adapter.trigger(element,event)
A framework independent event trigger, you may either use this or your framework's native event trigger.History.Adapter.onDomLoad(callback)
A framework independent onDomLoad binder, you may either use this or your framework's native onDomLoad binder.History.back()
Go back once through the history (same as hitting the browser's back button)History.forward()
Go forward once through the history (same as hitting the browser's forward button)History.go(X)
If X is negative go back through history X times, if X is positive go forwards through history X timesHistory.log(...)
Logs messages to the console, the log element, and fallbacks to alert if neither of those two existHistory.debug(...)
Same asHistory.log
but only runs ifHistory.debug.enable === true
window.onstatechange
Fired when the state of the page changes (does not include hashchanges)window.onanchorchange
Fired when the anchor of the page changes (does not include state hashes)
- History.js resolves the following browser bugs:
- Chrome does not retrieve the correct state data when traversing back to the start page
- Safari 5 and Safari iOS 4.2.1 do not fire the
onpopstate
event on page load or when the hash has changed - MSIE 6 and 7 sometimes do not actually apply hash-changes
- Non-Opera HTML4 browsers sometimes fail when the hash is not
urlencoded
- State data will always contain the State's title and url at:
data.title
anddata.url
- State data and title will not persist if the page was closed then re-opened, or navigated to another website then back - this is expected/standard functionality
- State titles will always be applied to the
document.title
if set - ReplaceState functionality is emulated in HTML4 browsers by discarding the replaced state, so when the discarded state is accessed it is skipped using the appropriate
History.back()
/History.forward()
call - HTML4 Browsers on initial page load will have a hash inserted into the url; this is to ensure correct cross-compatibility between HTML4 browsers (as IE will refresh the page if the anchor is lost)
- Changing the hash of the page causes
onpopstate
to fire; this is expected/standard functionality. To ensure correct compatibility between HTML5 and HTML4 browsers the following events have been created:window.onstatechange
: this is the same as onpopstate except does not fire for traditional anchorswindow.onanchorchange
: this is the same as onhashchange except does not fire for states
-
v1.5.0 - February 12 2011
- Moved to UglifyJS instead of Google Closure
- Split HTML4 functionality from HTML5 functionality
- Installation details have changed (the filenames are different)
-
v1.4.1 - February 10 2011
- Added HTML History API Support for Safari 5 and Safari iOS 4.2.1
- Cleaned code a bit (mostly with unit tests)
-
v1.4.0 - February 10 2011
- Unit Testing now uses QUnit
- Corrected Safari 5 Support
- Now uses queues instead of timeouts
- This means the API works exactly as expected, no more need to wrap calls in timeouts
- Included a Subscribe Form in the Demo for Version Updates via Email
- Small updates to Documentation
-
v1.3.1 - February 04 2011
- Improved Documentation
-
v1.3.0 - January 31 2011
- Support for cleaner HTML4 States
-
v1.2.1 - January 30 2011
- Fixed History.log always being called - reported by dlee
- Re-Added
History.go(index)
support
-
v1.2.0 - January 25 2011
- Support for HTML4 States in HTML5 Browsers (added test)
- Updates of Documentation
-
v1.1.0 - January 24 2011
- Developed a series of automated test cases
- Fixed issue with traditional anchors
- Fixed issue with differing replaceState functionality in HTML4 Browsers
- Fixed issue with Google Chrome artefacts being carried over to the initial state
- Provided
onstatechange
andonanchorchange
events
-
v1.0.0 - January 22 2011
- Supported
History.pushState
andHistory.replaceState
degradation - Supported jQuery, MooTools and Prototype Frameworks
- Supported
- Allow for url to be optional in
pushState
andreplaceState
calls - Add data persistance to HTML4 browsers (navigate to 3rd party website, navigate back, state data should still be there)
- Default will use cookies (requires no configuration) - limited to a 4KB payload.
- Extension will use cookies and add an ajax pre-fetch to fetch full (unlimited) data (requires configuration through a server-side helper)
- Add an Ajax extension to succeed the jQuery Ajaxy project
- Add a compilation test to ensure
.debug = false
and noHistory.log
orconsole.xxx
calls exist.
It's likely these features and/or others have been included in the latest dev branch. If you are wanting to fork and help out, then be sure to work on the dev branch and not master.