0

I'm trying to have a general function that allows to subscribe to any event. I want make sure that is only made one subscription for each event and element. So, before I add a new event listener, I remove the previous one.

The problem is, the way that I'm making it the previous event is not being remove and I don't know why.

Follows a working example:

function delegate(el, evt, sel, handler) {
  let eventListenerHandler = (event) => {
    var t = event.target;
      while (t && t !== this) {
          if (t.matches && t.matches(sel)) {
              handler.call(t, event);
          }
          t = t.parentNode;
      }
  }
  el.removeEventListener(evt, eventListenerHandler, evt === "focus" ? true : false);
  el.addEventListener(evt, eventListenerHandler, evt === "focus" ? true : false);
}
let consoleLog = () => console.log("WAS FOCUS");
delegate(document, "focus", "input", consoleLog);
delegate(document, "focus", "input", consoleLog);
<input/>
<input/>

As you can see in the example, each time an input is selected, is printing 2 times the message "WAS FOCUS" and want I want is to make sure that only one is printed.

Thank you in advance.

3
  • 2
    The two eventListenerHandlers across calls are two different function objects. You need to use the same one, or hold on separately to the existing one if the functions might be different. Commented Jul 12, 2018 at 17:16
  • @Ry- With my changed the issue remains. Can you tell me if the change that I made was what you suggested to do? Commented Jul 12, 2018 at 17:17
  • Sorry, I kind of glossed over the inner function declaration for a moment. Comment edited. Commented Jul 12, 2018 at 17:18

2 Answers 2

3

One relatively simple way to achieve what Ry suggested in comments, would be to store the handler function on the element itself - I've gone for an object here, so that handlers for different event types can be stored under the event name. (You only ever want to have one handler for one event type per element, right?)

And then if such a stored handler already exists, it is simply removed before it is overwritten with the new one.

Now storing such data directly "on" a DOM element is not considered good practice; not least because of possible collision with future specified properties of the same name. But in this case it is a very easy way to solve this; of course you could store the handler functions somewhere else, but you would still have to keep the reference to the DOM element around anyway, because you need to be able to differentiate between the handlers assigned to different elements as well.

function delegate(el, evt, sel, handler) {
  el.eventListenerHandler = el.eventListenerHandler || {};
  if(el.eventListenerHandler[evt]) {
    el.removeEventListener(evt, el.eventListenerHandler[evt], evt === "focus" ? true : false);
  }
  el.eventListenerHandler[evt] = (event) => {
    var t = event.target;
      while (t && t !== this) {
          if (t.matches && t.matches(sel)) {
              handler.call(t, event);
          }
          t = t.parentNode;
      }
  }
  el.addEventListener(evt, el.eventListenerHandler[evt], evt === "focus" ? true : false);
}
let consoleLog = () => console.log("WAS FOCUS");
delegate(document, "focus", "input", consoleLog);
delegate(document, "focus", "input", consoleLog);
<input/>
<input/>

Sign up to request clarification or add additional context in comments.

Comments

1

For removeEventListener(event,functionName) you have to provide same function to be removed which has been added previously.

Now here, each delegate(document, "focus", "input", consoleLog); create function eventListenerHandler on it's own scope and add it to addEventListener.

Basically two delegate() call create two different function eventListenerHandler with same functionality.

Now if we can save the previously added eventListenerHandler to a variable and use it in removeEventListener later then it will work.

Here is the example.

var temp = function(){};
    function delegate(el, evt, sel, handler) {
      let eventListenerHandler = (event) => {
        var t = event.target;
          while (t && t !== this) {
              if (t.matches && t.matches(sel)) {
                  handler.call(t, event);
              }
              t = t.parentNode;
          }
      }
      el.removeEventListener(evt, temp, evt === "focus" ? true : false);
      el.addEventListener(evt, eventListenerHandler, evt === "focus" ? true : false);
      temp = eventListenerHandler ;
    }
    let consoleLog = () => console.log("WAS FOCUS");
    delegate(document, "focus", "input", consoleLog,false);
    delegate(document, "focus", "input", consoleLog,false);
<input>
<input>

For better understanding you can refer to javascript Closure concept.

For your solution you have to save each function added as eventListener against it's event & element and remove that function in later call on that element with same event.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.