window bind POPSTATE
Asked Answered
S

8

29

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?

Seeger answered 14/1, 2011 at 5:10 Comment(0)
S
15

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.

Schoolteacher answered 1/4, 2011 at 16:27 Comment(1)
The HTML5 spec was changed in 2011 to state popstate should not fired on page load. Up-voting code.google.com/p/chromium/issues/detail?id=63040 is the best way to get Chrome back into compliance with the standard. Google has known for two years Chrome's behavior isn't correct.Bridegroom
D
45

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

Daly answered 11/5, 2011 at 3:13 Comment(0)
E
16

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 */ 
 } 
});
Expressive answered 15/5, 2012 at 14:56 Comment(0)
S
15

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.

Schoolteacher answered 1/4, 2011 at 16:27 Comment(1)
The HTML5 spec was changed in 2011 to state popstate should not fired on page load. Up-voting code.google.com/p/chromium/issues/detail?id=63040 is the best way to get Chrome back into compliance with the standard. Google has known for two years Chrome's behavior isn't correct.Bridegroom
G
3

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();
      }
    });
  }
}
Gehlenite answered 9/11, 2012 at 20:15 Comment(0)
D
2

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).

Disjunction answered 21/2, 2011 at 19:25 Comment(0)
S
1

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

Senegambia answered 20/11, 2015 at 10:4 Comment(0)
M
0

This is my workaround.

window.setTimeout(function() {
  window.addEventListener('popstate', function() {
    // ...
  });
}, 1000);
Monosyllable answered 4/12, 2011 at 20:40 Comment(3)
What does this do? Maybe instead of posting it everywhere you should it explain it for once.Firecrest
@drozzy Sure! This piece of code adds a new event listener to the popstate event (after 1 second). With this approach, the popstate event isn't fired on page's load.Monosyllable
@Baggz, according to a demo of Dive into html5, 1 ms is more appropriate.Unlock
T
0

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!

Thunder answered 7/5, 2015 at 9:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.