How to detect URL change in JavaScript

How can I check if a URL has changed in JavaScript? For example, websites like GitHub, which use AJAX, will append page information after a # symbol to create a unique URL without reloading the page. What is the best way to detect if this URL changes?

  • Is the onload event called again?
  • Is there an event handler for the URL?
  • Or must the URL be checked every second to detect a change?

In modern browsers (IE8+, FF3.6+, Chrome), you can just listen to the hashchange event on window.

In some old browsers, you need a timer that continually checks location.hash. If you're using jQuery, there is a plugin that does exactly that.

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, 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: */
history.pushState = ( f => function pushState(){
    var ret = f.apply(this, arguments);
    window.dispatchEvent(new Event('pushstate'));
    window.dispatchEvent(new Event('locationchange'));
    return ret;

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

    window.dispatchEvent(new Event('locationchange'))

use this code

window.onhashchange = function() { 

with jQuery

$(window).bind('hashchange', function() {

With jquery (and a plug-in) you can do

$(window).bind('hashchange', function() {
 /* things */

Otherwise yes, you would have to use setInterval and check for a change in the hash event (window.location.hash)

Update! A simple draft

function hashHandler(){
    this.oldHash = window.location.hash;

    var that = this;
    var detect = function(){
            alert("HASH CHANGED - new has" + window.location.hash);
            that.oldHash = window.location.hash;
    this.Check = setInterval(function(){ detect() }, 100);

var hashDetection = new hashHandler();

EDIT after a bit of researching:

It somehow seems that I have been fooled by the documentation present on Mozilla docs. The popstate event (and its callback function onpopstate) are not triggered whenever the pushState() or replaceState() are called in code. Therefore the original answer does not apply in all cases.

However there is a way to circumvent this by monkey-patching the functions according to @alpha123:

var pushState = history.pushState;
history.pushState = function () {
    pushState.apply(history, arguments);
    fireEvents('pushState', arguments);  // Some event-handling function

Original answer

Given that the title of this question is "How to detect URL change" the answer, when you want to know when the full path changes (and not just the hash anchor), is that you can listen for the popstate event:

window.onpopstate = function(event) {
  console.log("location: " + document.location + ", state: " + JSON.stringify(event.state));

Reference for popstate in Mozilla Docs

Currently (Jan 2017) there is support for popstate from 92% of browsers worldwide.

Add a hash change event listener!

window.addEventListener('hashchange', function(e){console.log('hash changed')});

Or, to listen to all URL changes:

window.addEventListener('popstate', function(e){console.log('url changed')});

This is better than something like the code below because only one thing can exist in window.onhashchange and you'll possibly be overwriting someone else's code.

// Bad code example

window.onhashchange = function() { 
     // Code that overwrites whatever was previously in window.onhashchange  

this solution worked for me:

var oldURL = "";
var currentURL = window.location.href;
function checkURLchange(currentURL){
    if(currentURL != oldURL){
        alert("url changed!");
        oldURL = currentURL;

    oldURL = window.location.href;
    setInterval(function() {
    }, 1000);


Although an old question, the Location-bar project is very useful.

var LocationBar = require("location-bar");
var locationBar = new LocationBar();

// listen to all changes to the location bar
locationBar.onChange(function (path) {
  console.log("the current url is", path);

// listen to a specific change to location bar
// e.g. Backbone builds on top of this method to implement
// it's simple parametrized Backbone.Router
locationBar.route(/some\-regex/, function () {
  // only called when the current url matches the regex

  pushState: true

// update the address bar and add a new entry in browsers history

// update the address bar but don't add the entry in history
locationBar.update("/some/url", {replace: true});

// update the address bar and call the `change` callback
locationBar.update("/some/url", {trigger: true});

To listen to url changes, see below:

window.onpopstate = function(event) {
  console.log("location: " + document.location + ", state: " + JSON.stringify(event.state));

Use this style if you intend to stop/remove listener after some certain condition.

window.addEventListener('popstate', function(e) {
   console.log('url changed')

While doing a little chrome extension, I faced the same problem with an additionnal problem : Sometimes, the page change but not the URL.

For instance, just go to the Facebook Homepage, and click on the 'Home' button. You will reload the page but the URL won't change (one-page app style).

99% of the time, we are developping websites so we can get those events from Frameworks like Angular, React, Vue etc..

BUT, in my case of a Chrome extension (in Vanilla JS), I had to listen to an event that will trigger for each "page change", which can generally be caught by URL changed, but sometimes it doesn't.

My homemade solution was the following :

var oldLength = -1;
function listen(currentLength) {
  if (currentLength != oldLength) {
    // Do your stuff here

  oldLength = window.history.length;
  setTimeout(function () {
  }, 1000);

So basically the leoneckert solution, applied to window history, which will change when a page changes in a single page app.

Not rocket science, but cleanest solution I found, considering we are only checking an integer equality here, and not bigger objects or the whole DOM.

Look at the jQuery unload function. It handles all the things.

The unload event is sent to the window element when the user navigates away from the page. This could mean one of many things. The user could have clicked on a link to leave the page, or typed in a new URL in the address bar. The forward and back buttons will trigger the event. Closing the browser window will cause the event to be triggered. Even a page reload will first create an unload event.

    function(event) {

window.addEventListener("beforeunload", function (e) {
    // do something
}, false);

You are starting a new setInterval at each call, without cancelling the previous one - probably you only meant to have a setTimeout

The answer below comes from here(with old javascript syntax(no arrow function, support IE 10+)):

(function() {
  if (typeof window.CustomEvent === "function") return false; // If not IE
  function CustomEvent(event, params) {
    params = params || {bubbles: false, cancelable: false, detail: null};
    var evt = document.createEvent("CustomEvent");
    evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
    return evt;
  window.CustomEvent = CustomEvent;

(function() {
  history.pushState = function (f) {
    return function pushState() {
      var ret = f.apply(this, arguments);
      window.dispatchEvent(new CustomEvent("pushState"));
      window.dispatchEvent(new CustomEvent("locationchange"));
      return ret;
  history.replaceState = function (f) {
    return function replaceState() {
      var ret = f.apply(this, arguments);
      window.dispatchEvent(new CustomEvent("replaceState"));
      window.dispatchEvent(new CustomEvent("locationchange"));
      return ret;
  window.addEventListener("popstate", function() {
    window.dispatchEvent(new CustomEvent("locationchange"));