This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.
The popstate event of the Window interface is fired when the active history entry changes while the user navigates the session history. It changes the current history entry to that of the last page the user visited or, if history.pushState() has been used to add a history entry to the history stack, that history entry is used instead.
Use the event name in methods like addEventListener(), or set an event handler property.
addEventListener("popstate", (event) => { })
onpopstate = (event) => { }
A PopStateEvent. Inherits from Event.
PopStateEvent.state Read only
Returns a copy of the information that was provided to pushState() or replaceState().
In addition to the Window interface, the event handler property onpopstate is also available on the following elements:
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.
These methods and their corresponding events can be used to add data to the history stack which can be used to reconstruct a dynamically generated page, or to otherwise alter the state of the content being presented while remaining on the same Document.
Note that just calling history.pushState() or history.replaceState() won't trigger a popstate event. The popstate event will be triggered by doing a browser action such as a click on the back or forward button (or calling history.back() or history.forward() in JavaScript).
Browsers tend to handle the popstate event differently on page load. Chrome (prior to v34) and Safari always emit a popstate event on page load, but Firefox doesn't.
Note: When writing functions that process popstate event it is important to take into account that properties like window.location will already reflect the state change (if it affected the current URL), but document might still not. If the goal is to catch the moment when the new document state is already fully in place, a zero-delay setTimeout() method call should be used to effectively put its inner callback function that does the processing at the end of the browser event loop: window.onpopstate = () => setTimeout(doSomeThing, 0);
It's important to first understand that — to combat unwanted pop-ups — browsers may not fire the popstate event at all unless the page has been interacted with.
This section describes the steps that browsers follow in the cases where they do potentially fire the popstate event (that is, in the cases where the page has been interacted with).
When a navigation occurs — either due to the user triggering the browser's Back button or otherwise — the popstate event is near the end of the process to navigate to the new location. It happens after the new location has loaded (if needed), displayed, made visible, and so on — after the pageshow event is sent, but before the persisted user state information is restored and the hashchange event is sent.
To better understand when the popstate event is fired, consider this simplified sequence of events that occurs when the current history entry changes due to either the user navigating the site or the history being traversed programmatically. Here, the transition is changing the current history entry to one we'll refer to as new-entry. The current page's session history stack entry will be referred to as current-entry.
Document, fetch the content and create its Document before continuing. This will eventually send events such as DOMContentLoaded and load to the Window containing the document, but the steps below will continue to execute in the meantime.pushState() or replaceState()), set the entry's title to the string returned by its document.title attribute.Document object than current-entry, the browsing context is updated so that its document property refers to the document referred to by new-entry, and the context's name is updated to match the context name of the now-current document.Document that has autocomplete configured with its autofill field name set to off is reset. See The HTML autocomplete attribute for more about the autocomplete field names and how autocomplete works.readyState is complete—and the document is not already visible, it's made visible and the pageshow event is fired at the document with the PageTransitionEvent's persisted attribute set to true.URL is set to that of new-entry.delta parameter on methods such as go()) is removed from the history stack.null, the document is scrolled to that fragment.History.state; otherwise, state is null.state changed, the popstate event is sent to the document.hashchange event to the window.As you can see, the popstate event is nearly the last thing done in the process of navigating pages in this way.
A page at http://example.com/example.html running the following code will generate logs as indicated:
window.addEventListener("popstate", (event) => {
console.log(
`location: ${document.location}, state: ${JSON.stringify(event.state)}`,
);
});
history.pushState({ page: 1 }, "title 1", "?page=1");
history.pushState({ page: 2 }, "title 2", "?page=2");
history.replaceState({ page: 3 }, "title 3", "?page=3");
history.back(); // Logs "location: http://example.com/example.html?page=1, state: {"page":1}"
history.back(); // Logs "location: http://example.com/example.html, state: null"
history.go(2); // Logs "location: http://example.com/example.html?page=3, state: {"page":3}"
The same example using the onpopstate event handler property:
window.onpopstate = (event) => {
console.log(
`location: ${document.location}, state: ${JSON.stringify(event.state)}`,
);
};
history.pushState({ page: 1 }, "title 1", "?page=1");
history.pushState({ page: 2 }, "title 2", "?page=2");
history.replaceState({ page: 3 }, "title 3", "?page=3");
history.back(); // Logs "location: http://example.com/example.html?page=1, state: {"page":1}"
history.back(); // Logs "location: http://example.com/example.html, state: null"
history.go(2); // Logs "location: http://example.com/example.html?page=3, state: {"page":3}"
Note that even though the original history entry (for http://example.com/example.html) has no state object associated with it, a popstate event is still fired when we activate that entry after the second call to history.back().
| Specification |
|---|
| HTML> # event-popstate> |
| HTML> # handler-window-onpopstate> |
| Desktop | Mobile | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Chrome | Edge | Firefox | Opera | Safari | Chrome Android | Firefox for Android | Opera Android | Safari on IOS | Samsung Internet | WebView Android | WebView on iOS | |
popstate_event |
5Before version 34, Chrome would fire apopstate event on page load. |
12 | 4 | 11.5 | 5Before version 10, Safari would fire apopstate event on page load. |
18Before version 34, Chrome Android would fire apopstate event on page load. |
4 | 11.5 | 4.2Before version 10, Safari on iOS would fire apopstate event on page load. |
1.0Before version 2.0, Samsung Internet would fire apopstate event on page load. |
4.4Before version 37, WebView Android would fire apopstate event on page load. |
4.2Before version 10, WebView on iOS would fire apopstate event on page load. |
© 2005–2025 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/API/Window/popstate_event