There is no JavaScript event for a URL change. There is one, however, for when the fragment changes (the part after the # symbol), called hashchange, and there is another, popstate, which doesn't always get triggered, for when the user clicks on the back or forward buttons or the history.back() or history.go() methods are called.

To detect if a URL changes, there are a couple of arguably hacky options:

  1. Store the URL in a variable and compare it against location.href every once in a while (polling) to see if it changed:
function doSomething() {
  console.log('URL change detected!');
}

let currentUrl = location.href;

setInterval(() => {
  if (location.href !== currentUrl) {
    currentUrl = location.href;
    doSomething();
  }
}, 500);
  1. Replace the history.* methods with custom ones that trigger a manual event (this is uglier in my opinion, and I haven't tested it much):
history.pushState = ( f => function pushState(){
    var ret = f.apply(this, arguments);
    window.dispatchEvent(new Event('pushstate'));
    window.dispatchEvent(new Event('locationchange'));
    return ret;
})(history.pushState);

history.replaceState = ( f => function replaceState(){
    var ret = f.apply(this, arguments);
    window.dispatchEvent(new Event('replacestate'));
    window.dispatchEvent(new Event('locationchange'));
    return ret;
})(history.replaceState);

window.addEventListener('popstate',()=>{
    window.dispatchEvent(new Event('locationchange'))
});
Final thoughts

Both methods strike me as awful from an "engineering elegance" point of view, but what is web development if not an endless sequence of terrible decisions?

Previous on JavaScript
Mastodon Mastodon