Given the following:
$(window).bind("popstate", function() {
alert('popstate');
});
On first load, the alert fires with FireFox and Chrome but not Safari. Why is that? Anyone else seen this and know how to best solve for this?
Given the following:
$(window).bind("popstate", function() {
alert('popstate');
});
On first load, the alert fires with FireFox and Chrome but not Safari. Why is that? Anyone else seen this and know how to best solve for this?
The situation is now reversed. Chrome has fixed the bug and now fires popstate on page load but Firefox 4 (since RC) has departed from the spec and now does not fire popstate!
UPDATE: The HTML5 spec was changed in 2011 to state popstate should not fired on page load. Both Firefox and Chrome now do the right thing as of Firefox 4 and Chrome 34.
See the code from pjax. pjax is fairly popular open source library now, so the below logic might be the best to avoid this issue.
var popped = ('state' in window.history), initialURL = location.href
$(window).bind('popstate', function(event) {
// Ignore inital popstate that some browsers fire on page load
var initialPop = !popped && location.href == initialURL
popped = true
if ( initialPop ) return
...
https://github.com/defunkt/jquery-pjax/blob/master/jquery.pjax.js
In webkit the popstate event is fired on page load. To avoid this, this easy work around works for me:
Every time I fire history.push(...)
I add the class
historypushed to the body
-tag:
history.push(...);
$("body").addClass("historypushed");
When I trigger the popstate event, I check for this class:
$(window).bind('popstate', function(e) {
if($("body").hasClass("historypushed")) {
/* my code */
}
});
An easy way to avoid this issue is to set the first argument on pushState to true then check against onpopstate. Similar to the pjax example but a bit more straightforward. The below example will run doSomething() for every popstate event except for the first page load.
function setupPopState() {
if (history.popState) {
# immediately replace state to ensure popstate works for inital page
history.replaceState(true, null, window.location.pathname);
$(window).bind('popstate', function(event) {
if (event.originalEvent.state) {
doSomething();
}
});
}
}
There was a bug in Webkit that incorrectly implemented the "popstate" event. Check out this simple post explaining the problem (cool little show and tell): http://www.bcherry.net/playground/pushstate
My suggestion would be to implement your own "popstate" event tracker for Safari. Something like this:
$(window).load(function(){
function fire_popstate(){
$(this).trigger("popstate"); // fire it when the page first loads
}
var lasthash = window.location.hash;
setInterval(function(){
var currenthash = window.location.hash;
if(lasthash != currenthash){
fire_popstate();
}
}, 500);//check every half second if the url has changed
});
You could wrap that statement in a browser test to check for safari. Even better see if "popstate" has been fired by the time the DOM is ready and then apply the inner function to replace the implementation. The one thing you don't want to happen is have two popstate events to be fired (duplicating your event handler logic, great way to lock up the UI).
This is my workaround.
window.setTimeout(function() {
window.addEventListener('popstate', function() {
// ...
});
}, 1000);
I convert @Nobu & @Tamlyn answers into an object, I also add a little fix by adding "window.history.state !== null". In some browsers the history.state exists, but it's null so the it was not working.
/**
* The HTML5 spec was changed in 2011 to state popstate should not
* fired on page load. Chrome(34) & Firefox(4) has fixed the bug but
* some browsers (e.g. Safari 5.1.7) are still fire the popstate on
* the page load. This object created from the Pjax Library to handle
* this issue.
*/
var popstatePageloadFix = {
popped : ('state' in window.history && window.history.state !== null),
initialUrl : location.href,
initialPop : false,
init : function() {
this.initialPop = !this.popped && location.href == this.initialUrl;
this.popped = true;
return this.initialPop;
}
};
$(window).on("popstate", function (event) {
// Ignore initial popstate that some browsers fire on page load
if ( popstatePageloadFix.init() ) return;
...
});
Thanks @Nobu! Thanks @Tamlyn!
This answer to a similar question suggests to check for boolean truth of event.state
in the popstate
event handler:
window.addEventListener('popstate', function(event) {
if (event.state) {
alert('!');
}
}, false);
You can also tie your callback function to popstate
event like this:
window.onpopstate = callback();
Check here for more information on that solution