4

I need to trigger a function whenever window.location.href changes but I'm running into problems. I went over the source for various watch polyfills, but I can't quite follow what was going on with the code.

if (!Object.prototype.watch) {
  Object.defineProperty(Object.prototype, "watch", {
    enumerable: false
    , configurable: true
    , writable: false
    , value: function (prop, handler) {
      var
        oldval = this[prop]
        , newval = oldval
        , getter = function () {
          return newval;
        }
        , setter = function (val) {
          oldval = newval;
          return newval = handler.call(this, prop, oldval, val);
        }
        ;
      if (delete this[prop]) { // can't watch constants
        Object.defineProperty(this, prop, {
          get: getter
          , set: setter
          , enumerable: true
          , configurable: true
        });
      }
    }
  });
}

Obviously, I don't know much about the internals of JS or reflection in general. Here is a method that works for title:

var observer = new window.MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    change({title: mutation.target.textContent});
  });
});

observer.observe(document.querySelector('head > title'),
  { subtree: true, characterData: true, childList: true });

But I can't specify Location via a query selector and I'm pretty sure it would need to implement the node class for observer to work.

Indolering
  • 3,058
  • 30
  • 45

2 Answers2

2

In non-Firefox browsers, you cannot set a watcher on location.href. I needed to set the watcher on location.pathway, location.hash, and location.search instead.

It is also better to just reuse a function instead of writing the same one out twice. I was able to shave it down to just this:

if (!Object.prototype.watch) { //don't overwrite gecko watch function
  Object.prototype.watch = function(prop, handler) {
    var oldval = this[prop], newval = oldval,
      getter = function() {
        return newval;
      },
      setter = function(val) {
        oldval = newval;
        return newval = handler.call(this, prop, oldval, val);
      };
    if (delete this[prop]) {
      Object.defineProperty(this, prop, {
        get: getter,
        set: setter
      });
    }
  };
}
Indolering
  • 3,058
  • 30
  • 45
1

This is a little bit of a hack, but not much more than anything else I have seen and it will work pretty much equally in all modern browsers.

var myTitle = document.title;
var myLocation = window.location.href;

setInterval(function() {
    if (myTitle != document.title || myLocation != window.location.href)
    {
        // Do something here;

        myTitle = document.title;
        myLocation = window.location.href;         
    }
}, 100);

It simply polls the properties in short intervals. To short to be visible for the user but it should also not have much CPU overhead.

ced-b
  • 3,957
  • 1
  • 27
  • 39