So now that HTML5 introduces history.pushState
to change the browsers history, websites start using this in combination with Ajax instead of changing the fragment identifier of the URL.
Sadly that means that those calls cannot be detect anymore by onhashchange
.
My question is: Is there a reliable way (hack? ;)) to detect when a website uses history.pushState
? The specification does not state anything about events that are raised (at least I couldn't find anything).
I tried to create a facade and replaced window.history
with my own JavaScript object, but it didn't have any effect at all.
Further explanation: I'm developing a Firefox add-on that needs to detect these changes and act accordingly.
I know there was a similar question a few days ago that asked whether listening to some DOM events would be efficient but I would rather not rely on that because these events can be generated for a lot of different reasons.
Update:
Here is a jsfiddle (use Firefox 4 or Chrome 8) that shows that onpopstate
is not triggered when pushState
is called (or am I doing something wrong? Feel free to improve it!).
Update 2:
Another (side) problem is that window.location
is not updated when using pushState
(but I read about this already here on SO I think).
According to this, there is no reason for popstate to be fired when you use
pushState
. But an event such aspushstate
would come in handy. Becausehistory
is a host object, you should be careful with it, but Firefox seems to be nice in this case. This code works just fine:Your jsfiddle becomes:
You can monkey-patch
window.history.replaceState
in the same way.Note: of course you can add
onpushstate
simply to the global object, and you can even make it handle more events viaadd/removeListener
You could bind to the
window.onpopstate
event?https://developer.mozilla.org/en/DOM%3awindow.onpopstate
From the docs:
As standard states:
we need to call history.back() to trigeer WindowEventHandlers.onpopstate
So insted of:
do:
Since you're asking about a Firefox addon, here's the code that I got to work. Using
unsafeWindow
is no longer recommended, and errors out when pushState is called from a client script after being modified:Instead, there's an API called exportFunction which allows the function to be injected into
window.history
like this:Well, I see many examples of replacing the
pushState
property ofhistory
but I'm not sure that's a good idea, I'd prefer to create a service event based with a similar API to history that way you can control not only push state but replace state as well and it open doors for many other implementations not relying on global history API. Please check the following example:If you need the
EventEmitter
definition, the code above is based on the NodeJS event emitter: https://github.com/nodejs/node/blob/36732084db9d0ff59b6ce31e839450cd91a156be/lib/events.js.utils.inherits
implementation can be found here: https://github.com/nodejs/node/blob/36732084db9d0ff59b6ce31e839450cd91a156be/lib/util.js#L970I think this topic needs a more modern solution.
I'm sure
nsIWebProgressListener
was around back then I'm surprised no one mentioned it.From a framescript (for e10s compatability):
Then listening in the
onLoacationChange
That will apparently catch all pushState's. But there is a comment warning that it "ALSO triggers for pushState". So we need to do some more filtering here to ensure it's just pushstate stuff.
Based on: https://github.com/jgraham/gecko/blob/55d8d9aa7311386ee2dabfccb481684c8920a527/toolkit/modules/addons/WebNavigation.jsm#L18
And: resource://gre/modules/WebNavigationContent.js