I put all of my code in $(document).ready as per norm, but should I put my 'popstate' listener at the very end of this code too? Or does it matter?
This doesn't really matter, and since it's an event, can even be done before your ready method. The only thing needing to be placed inside document ready is code interacting with the DOM. Everything else doesn't (and possibly shouldn't) be placed in document ready.
Example:
window.onpopstate = function() {
// binding this event can be done anywhere,
// but shouldn't be inside document ready
};
$(document).ready(function() {
// DOM manipulation and other stuff
});
Now when popstate actually is triggered is a lot different than when it is bound. According to the Mozilla doc:
A popstate event is dispatched to the window every time the active history entry changes. If the history entry being activated was created by a call to history.pushState() or was affected by a call to history.replaceState(), the popstate event's state property contains a copy of the history entry's state object.
I was confused, because popstate was firing on every page load (I use chrome).
From https://developer.mozilla.org/en-US/docs/DOM/window.onpopstate:
Browsers tend to handle the popstate event differently on page load.
Chrome and Safari always emit a popstate event on page load, but
Firefox doesn't.
So in chrome (and apparently safari), the jquery ready method will execute, followed by the popstate event.
i.e. it doesn't matter if you attach the event within ready (or body.onload for that matter), the popstate event will happen after your ready method is complete.
In firefox (16.0.1) the popstate event does not fire on page loads (I can't test IE 10).
https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onpopstate
Calling history.pushState() or history.replaceState() won't trigger a popstate event. The popstate event is only triggered by performing a browser action, such as clicking on the back button (or calling history.back() in JavaScript), when navigating between two history entries for the same document.
Just add a key to the state every time you push to history or replacing the state, and check for it on the popstate event, if you don't use the state at all you can simply push {} as state and quit the pop state in case the event.state is null.
Related
I am trying to remove dynamically an event from the DOM. For this, I am using Chrome (version 79) dev tools, on page https://www.omgubuntu.co.uk/category/news?utm_source=menu
In the Elements tab, I go to Event Listeners, and look for the 'load' event, and I find 3 events on the Window element. So in the console, I type:
$(window).off('load');
The command executes with no error, but nothing seemed to have changed, even when I press the refresh button in the events section. I am expecting all the 'load' events for Window to disappear.
I have this same problem on many other sites, but works fine on my local webpage (this is how I know I am using JQuery the right way)...
.off() only removes events that were added via the .on() method, so any events added with other APIs wouldn't be affected.
Now, when you say even when I press the refresh button in the events section. I am expecting all the 'load' events for Window to disappear., you misunderstand the nature of how a page is processed. If you refresh the page, all of its code is processed again, therefore even if you were successful in removing the handlers in your console, they would all come back because the page is starting all over again.
Understand that what you do in the console only affects the current instance of the page loaded within the browser.
To remove an existing listener, use getEventListeners command line API:
getEventListeners(window).load.forEach(x=>window.removeEventListener('load',x.listener))
or simply click the Remove button in Elements Event listeners panel:
My popstate event handler is defined thusly:
window.addEventListener('popstate', function (ev) {
alert('popstate');
/* recompose dom */
});
It works fine while I am within my site as I use back and forward, however when I navigate to another site and then come back my event does not fire.
I assume this is by design based on the following comment on Mozilla Developer:
Browsers tend to handle the popstate event differently on page load.
Chrome (prior to v34) and Safari (prior to 10.0) always emit a
popstate event on page load, but Firefox doesn't.
If this is the case, how do I get the most recent pushed state when coming back to my page from another site.
I figured it out; pull the current page state from history.state on document ready and apply it.
I'm playing with window.onpopstate, and there is a thing that annoys me:
Browsers tend to handle the popstate event differently on page load.
Chrome and Safari always emit a popstate event on page load, but
Firefox doesn't.
source
I tested it, and yeah, in Chrome and Safari 5.1+ the popstate event is fired on page load, but not in Firefox or IE10.
The problem is, that I want to listen only to popstate events where user clicked the back or forward button (or the history was changed via javascript), but don't want to do anything on pageload.
In other words, I want to differentiate the popstate event from page load from the other popstate events.
This is what I tried so far (I'm using jQuery):
$(function() {
console.log('document ready');
setTimeout(function() {
window.onpopstate = function(event) {
// Do something here
}, 10);
});
Basically I'm try to bind my listener function to popstate late enough to be not bound on page load, only later.
This seems to work; however, I don't like this solution. How can I be sure that the timeout chosen for setTimeout is big enough, but not too big (because I don't want it to wait too much).
I hope for a smarter solution!
Check for boolean truth of event.state in popstate event handler:
window.addEventListener('popstate', function(event) {
if (event.state) {
alert('!');
}
}, false);
To ensure this will work, always specify a non-null state argument when calling history.pushState() or history.replaceState(). Also, consider using a wrapper library like History.js that provides consistent behavior across browsers.
I had a similar problem and i had to validate to make sure if page was loaded completely.
I used something like this :
var page_loaded = false;
window.onpopstate = function(event){
if(!page_loaded){
page_loaded = true;
return false;
}
//Continue With Your Code
}
To react on popstate event, you need to push some state onto the session history.
For example add this line to the document ready section:
history.pushState(null, null, window.location.pathname);
Not ideal, but it works in Chrome, Firefox and other browsers as well.
Then the event is fired correctly when user clicks on Back or Forward button, also when history.back(), history.forward(), history.go() methods are called manually. Each time when popstate has been invoked, you have to push another state again to make it working.
See also:
Single-Page Apps and HTML5 pushState
How to Detect Browser Back Button event - Cross Browser
It seems none of the browsers are emitting the event on page load any more as of today:
Browsers used to handle the popstate event differently on page load, but now they behave the same. Firefox never emitted a popstate event on page load. Chrome did until version 34, while Safari did until version 10.0.
Source: https://developer.mozilla.org/en-US/docs/Web/API/PopStateEvent
HTML5 has a pageshow event. In what instances does it work differently than the body's onload handler?
The pageshow event fires every time the page is loaded whereas the load event doesn’t fire in Firefox 1.5 when the page is loaded from cache.
In addition to the answer above, there is a far more important difference between the two.
According to my test on iOS, if you load a local page from a WkWebView, hit a link to jump to another page and use the goBack function to go back the previous page, only onpageshow will fire, onload will not.
I'm trying to create a Chrome plugin for facebook and I'm using onpopstate event to check when the user goes to another page. The only problem is that the onpopstate doesn't fire.
This is the (simple) code I'm using:
window.onpopstate = function() { console.log('pop'); };
this is a screen of the problem:
As you can see the pushState code is called, but the onpopstate listener is not.
Do you know what's happening here?
#enhzflep's answer is in the comments above and not immediately obvious. From MDN:
Calling history.pushState() or history.replaceState() won't trigger a popstate event. The popstate event is only triggered by performing a browser action, such as clicking on the back button (or calling history.back() in JavaScript), when navigating between two history entries for the same document.
In other words, the onpopstate event shouldn't be firing in this case.