2

I want to check if every time the URL has changed, I'd fire an event. Since, there's no pushstate event I'm following this advice How to get notified about changes of the history via history.pushState?

(function(history) {
  var pushState = history.pushState;
  history.pushState = function(state) {
    if (typeof history.onpushstate == "function") {
      history.onpushstate({
        state: state
      });
    }
    return pushState.apply(history, arguments);
  }

  function e(event) {
    console.log("location: " + document.location + ", state: " + JSON.stringify(event.state));
  }
  window.addEventListener('popstate', e);
  history.onpushstate = e;
})(window.history)

However, my lib allows mutliple instances. If I initialize my library multiple times, I'd get messages according to how many times the the library has been initialized. Is there anyway I can make sure that only one event is attached even though I run that snippet multiple times?

Community
  • 1
  • 1
toy
  • 11,711
  • 24
  • 93
  • 176
  • You could use a guard variable, such as `window.pushStateEventCaptured` and check its value before adding an event listener. – Phylogenesis Aug 30 '16 at 16:14
  • 2
    AFAIK there's no native JS way to retrieve listeners on the given DOM element (which would be more elegant solution). Having said that, what prevents you from doing `if (!window.isHistoryApplied)(function(history){ window.isHistoryApplied = true`? – eithed Aug 30 '16 at 16:15
  • Have you checked this because [any **exact** duplicates should be discarded](http://stackoverflow.com/a/28607536/542251) – Liam Aug 30 '16 at 16:15
  • @Liam unless the references are to the exact same function object they won't be counted as exact duplicates. – Alnitak Aug 30 '16 at 16:18
  • Possible duplicate of [Javascript event addEventListener registering multiple times for same function; using OOP Javascript](http://stackoverflow.com/questions/26127336/javascript-event-addeventlistener-registering-multiple-times-for-same-function) – Liam Aug 30 '16 at 16:19
  • you can always set a global flag... `if(!window.hasEventBeenAttached){ function(){/* do stuff */ }; window.hasEventBeenAttached=true; }` – Robert Parham Aug 30 '16 at 16:19

1 Answers1

1

One way to accomplish this might be through the use of an intermediary object. This approach would allow you control over the event in the instance that you want to remove it and can be extended easily for other similar use cases. I've added a public isBound boolean incase of an instance where you would want to verify that the event has been subscribed to.

(function(history) {
  var pushState = history.pushState;

  function e(event) {
    console.log("location: " + document.location + ", state: " + JSON.stringify(event.state));
  }

  function proxyPopstateEvent (evt) {
    history.pushState = function(state) {
      if (typeof history.onpushstate == "function") {
        history.onpushstate({
          state: state
        });
      }
      return pushState.apply(history, arguments);
    }

    var proxy = function (e) {
      this.event = e;
      this.isBound = false;

      this.bindPopstateEvent = function() {
        window.addEventListener('popstate', this.event);
        history.onpushstate = this.event;
        this.isBound = true;
      }

      this.removePopstateEvent = function() {
        window.removeEventListener('popstate', this.event);
        history.onpushstate = {};
      }
    };
    window.popstateEventProxy = new proxy(evt);
    window.popstateEventProxy.bindPopstateEvent();
  }

  if (!window.popstateEventProxy) {
    proxyPopstateEvent(e);
  }
})(window.history)
Matt
  • 4,107
  • 3
  • 29
  • 38
  • Thanks it works. So it seems like there's no way to achieve this without using global `window` object? – toy Aug 30 '16 at 17:08
  • Perhaps with ES6, I haven't looked into that possibility. Since you are designing a library I assumed an ES5 constraint, it seems a little unlikely in a library setting though. But I feel your pain, it's always a shame to pollute the global namespace. – Matt Aug 30 '16 at 17:12