Intercepting call to the back button in my AJAX application
Ah, the back button. You might imagine "back" fires a JavaScript event which you could simply cancel like so:
document.onHistoryGo = function() { return false; }
No so. There simply is no such event.
If you really do have a web app (as opposed to just a web site with some ajaxy features) it's reasonable to take over the back button (with fragments on the URL, as you mention). Gmail does this. I'm talking about when your web app in all in one page, all self-contained.
The technique is simple — whenever the user takes action that modifies things, redirect to the same URL you're already on, but with a different hash fragment. E.g.
window.location.hash = "#deleted_something";
...
window.location.hash = "#did_something_else";
If the overall state of your web app is hashable, this is a great place to use a hash. Say you have a list of emails, maybe you'd concatenate all their IDs and read/unread statuses, and take an MD5 hash, using that as your fragment identifier.
This kind of redirect (hash only) doesn't try to fetch anything from the server, but it does insert a slot in the browser's history list. So in the example above, user hits "back" and they're now showing #deleted_something in the address bar. They hit back again and they're still on your page but with no hash. Then back again and they actually go back, to wherever they came from.
Now the hard part though, having your JavaScript detect when the user hit back (so you can revert state). All you do is watch the window location and see when it changes. With polling. (I know, yuck, polling. Well, there's nothing better cross-browser right now). You won't be able to tell if they went forward or back though, so you'll have to get creative with your hash identifiers. (Perhaps a hash concatenated with a sequence number...)
This is the gist of the code. (This is also how the jQuery History plugin works.)
var hash = window.location.hash;
setInterval(function(){
if (window.location.hash != hash) {
hash = window.location.hash;
alert("User went back or forward to application state represented by " + hash);
}
}, 100);
To give an up-to-date answer to an old (but popular) question:
HTML5 introduced the
history.pushState()
andhistory.replaceState()
methods, which allow you to add and modify history entries, respectively. These methods work in conjunction with thewindow.onpopstate
event.Using
history.pushState()
changes the referrer that gets used in the HTTP header forXMLHttpRequest
objects created after you change the state. The referrer will be the URL of the document whose window isthis
at the time of creation of theXMLHttpRequest
object.
Source: Manipulating the browser history from Mozilla Developer Network.
Using jQuery I've made a simple solution:
$(window).on('hashchange', function() {
top.location = '#main';
// Eventually alert the user describing what happened
});
So far only tested in Google Chrome though.
This solved the problem for my web app which is also highly AJAX-based.
It is perhaps a little hack'ish - but I'd call it elegant hacking ;-) Whenever you try to navigate backwards it pops a hash part in the URI which is technically what it then tries to navigate backwards over.
It intercepts both clicking the browser button and mouse button. And you can't bruteforce it backwards either by clicking several times a second, which is a problem that would occur in solutions based on setTimeout or setInterval.
I really appreciate the explanation given in darkporter's answer, but I think it can be improved by using a "hashchange" event. As darkporter explained, you want to make sure that all of your buttons change the window.location.hash value.
- One way is to use
<button>
elements, and then attach events to them that then setwindow.location.hash = "#!somehashstring";
. - Another way to do this is to just use links for your buttons, like
<a href="#!somehashstring">Button 1</a>
. The hash is automatically updated when these links are clicked.
The reason I have an exclamation mark after the hash sign is to satisfy Google's "hashbang" paradigm (read more about that), which is useful if you want to be indexed by search engines. Your hashstring will typically be name/value pairs like #!color=blue&shape=triangle
or a list like #!/blue/triangle
-- whatever makes sense for your web app.
Then you only need to add this bit of code which will fire whenever the hash value changes (including when the back button is hit). No polling loop seems to be necessary.
window.addEventListener("hashchange", function(){
console.log("Hash changed to", window.location.hash);
// .... Do your thing here...
});
I haven't tested in anything but Chrome 36, but according to caniuse.com, this should be available in IE8+, FF21+, Chrome21+, and most other browsers except Opera Mini.
It is very easy toi disable the browser BACK button, like the JQuery code below:
// History API
if( window.history && window.history.pushState ){
history.pushState( "nohb", null, "" );
$(window).on( "popstate", function(event){
if( !event.originalEvent.state ){
history.pushState( "nohb", null, "" );
return;
}
});
}
You can see it working and more examples here dotnsf and here thecssninja
Thanks !