Update 2024:
Major browsers should now support the Navigation API, so it can be done like this:
window.navigation.addEventListener("navigate", (event) => {
console.log('location changed!');
})
API Reference: https://developer.mozilla.org/en-US/docs/Web/API/Navigation_API
Previous Method (without the Navigation API):
I wanted to be able to add locationchange
event listeners. After the modification below, we'll be able to do it, like this:
window.addEventListener('locationchange', function () {
console.log('location changed!');
});
In contrast, window.addEventListener('hashchange',() => {})
would only fire if the part after a hashtag in a url changes, and window.addEventListener('popstate',() => {})
doesn't always work.
This modification, similar to Christian's answer, modifies the history object to add some functionality.
By default, before these modifications, there's a popstate
event, but there are no events for pushstate
, and replacestate
.
This modifies these three functions so that all fire a custom locationchange
event for you to use, and also pushstate
and replacestate
events if you want to use those.
These are the modifications:
(() => {
let oldPushState = history.pushState;
history.pushState = function pushState() {
let ret = oldPushState.apply(this, arguments);
window.dispatchEvent(new Event('pushstate'));
window.dispatchEvent(new Event('locationchange'));
return ret;
};
let oldReplaceState = history.replaceState;
history.replaceState = function replaceState() {
let ret = oldReplaceState.apply(this, arguments);
window.dispatchEvent(new Event('replacestate'));
window.dispatchEvent(new Event('locationchange'));
return ret;
};
window.addEventListener('popstate', () => {
window.dispatchEvent(new Event('locationchange'));
});
})();
Note, we're creating a closure, to save the old function as part of the new one, so that it gets called whenever the new one is called.