How to remove a "listener" that I added via addEventListener?

8

I usually use the addEventListener function to "listen" to some events, such as click , Websocket connection, and the like.

For example:

var ws = new WebSocket('ws://echo.websocket.org/');

ws.addEventListener('open', function () {
       // Minha lógica
});

if (window.alguma_condicao) {
    ws.addEventListener('open', function () {
         // Minha lógica
    });
}

In the above case, in a given situation, how could I remove all the "listeners" (the callbacks, which are the anonymous functions) that I added through addEventListener ?

    
asked by anonymous 28.09.2017 / 21:38

3 answers

9

You can only remove a listener by using the reference to the function. That is, you can not remove events that are created with anonymous functions.

To remove the listeners using the references, simply use the removeEventListener .

In this case, be aware of the parameter useCapture (the third). If addEventListener uses this parameter as true , removeEventListener needs to do the same, otherwise the event will not be removed.

Illustrative example:

const bt = document.getElementById('bt');

bt.addEventListener('click', fnClick);

function fnClick() {
  console.log('Essa é a última vez que você fez isso');
  bt.removeEventListener('click', fnClick);
}
<button id="bt">Clique aqui</button>

Another option is to remove the event inside the function that adds it. This is possible by defining a named function and using the name of this function to remove the listener or using arguments.callee .

const bt = document.getElementById('bt');
const bt2 = document.getElementById('bt2');

bt.addEventListener('click', function fn() {
  console.log('Essa é a última vez que você fez isso');
  bt.removeEventListener('click', fn);
});

bt2.addEventListener('click', function() {
  console.log('Essa é a última vez que você fez isso (2)');
  bt2.removeEventListener('click', arguments.callee);
});
<button id="bt">Clique aqui</button>
<button id="bt2">Clique aqui</button>
    
28.09.2017 / 21:47
7

Unfortunately, Javascript does not provide a way to list all events, so you'll need to keep a reference to the events you've added.

Below is an example of how to remove a specific event.

let teste = document.getElementById("teste");
let event1 = function () {
  console.log("evento 1");
}

let event2 = function () {
  console.log("evento 2");
}

let event3 = function () {
  console.log("evento 3");
}

teste.addEventListener("click", event1);
teste.addEventListener("click", event2);
teste.addEventListener("click", event3);

teste.removeEventListener("click", event2);
<button id="teste">Click Me</button>

You can implement your own Event Manager.

let EventHandler = (function () {
  let EventHandler = function (objeto) {
    this.objeto = objeto;  
    this.types = {};
  };
  EventHandler.prototype.addEvent = function (type, listener) {
    if (!this.types[type])
      this.types[type] = [];
    this.types[type].push(listener);
    this.objeto.addEventListener(type, listener);
  };
  EventHandler.prototype.removeEvent = function (type, listener) {
    if (!this.types[type])
      return;
      
    var index = this.types[type].indexOf(listener);
    this.types[type].splice(index);
    this.objeto.removeEventListener(type, listener);
  };
  EventHandler.prototype.removeAllEvent = function (type) {
    if (!this.types[type])
      return;
    this.types[type].forEach(function (listener, indice) {
      this.objeto.removeEventListener(type, listener);
    }.bind(this));
    this.types[type].length = 0;
  }
  return EventHandler;
})();

let teste = document.getElementById("teste");
let handler = new EventHandler(teste);

handler.addEvent("click", function () { console.log('event 1'); });
handler.addEvent("click", function () { console.log('event 2'); });
handler.addEvent("click", function () { console.log('event 3'); });

teste.addEventListener("click", function () {
  handler.removeAllEvent("click");
  console.log('button clicado');
});
<button id="teste">Click Me</button>
    
28.09.2017 / 21:47
6

A very trivial way of executing the task is to intercept the call of the addEventListener function and save the reference to the function in some way, since, as stated in the other answers, it would not be possible remove an anonymous function (or arrow function ) since we would not have the reference of this. If we store the reference, the solution becomes possible.

Considering that such a solution might be necessary in a production application, it would not be interesting to have to modify the whole code, such as naming all anonymous functions or calling our custom function instead of addEventListener . The most practical would be if we define such behavior as the default of the addEventListener function and this is possible by reimplementing it through Element.prototype.addEventListener ". However, since we will need the original function, we need to create a backup of this with another name:

Element.prototype._AddEventListener = Element.prototype.addEventListener;
  

Here, the function addEventListener original becomes _addEventListener .

Then we define our custom addEventListener function:

Element.prototype.addEventListener = function(type, listener, useCapture = false) {

    if (typeof this.listeners === "undefined") {
        this.listeners = [];
    }

    this.listeners.push({type: type, listener: listener, useCapture: useCapture});
    this._AddEventListener(type, listener, useCapture);
};

That is, when the function is called, it will be checked whether the element has a listeners attribute and will store all the references of the listeners added. If it does not, it is defined as an empty list, and then added the listener reference to the list, also storing the event name and useCapture value. At the end, it is called the _addEventListener function, which is the original function, so the original behavior of the function remains unchanged.

Now, to remove all listeners from an element, we can define a new function:

EventTarget.prototype.removeEventListeners = function(type = null, useCapture = false) {
    if (typeof this.listeners !== "undefined") {
        for (let i = 0; i < this.listeners.length; i++) {
            if ((type == this.listeners[i].type || type === null) && useCapture == this._listeners[i].useCapture) {
                this.removeEventListener(type, this.listeners[i].listener, useCapture);
            }
        }
    }
};

Realize s in removeEventListeners so as not to conflict with the original function. Basically the function checks if the element has a list of listeners and, if it has, it traverses it by removing all listeners matching the parameters of the function. That is, if you set the value of type , all the listeners associated with the event defined by type whose useCapture vajor is the same as the one passed by parameter. If it is called without parameters, all listeners of all events with useCapture fault will be removed.

Let's see in practice:

// Mantemos a função original de addEventListener:
EventTarget.prototype.originalAddEventListener = EventTarget.prototype.addEventListener;

// Definimos uma função nova que armazena a referência dos listeners:
EventTarget.prototype.addEventListener = function(type, listener, useCapture = false) {

    if (typeof this._listeners === "undefined") {
        this._listeners = [];
    }
    
    this._listeners.push({type: type, listener: listener, useCapture: useCapture});
    this.originalAddEventListener(type, listener, useCapture);
};

EventTarget.prototype.removeEventListeners = function(type = null, useCapture = false) {
    if (typeof this._listeners !== "undefined") {
        for (let i = 0; i < this._listeners.length; i++) {
            if ((type == this._listeners[i].type || type === null) && useCapture == this._listeners[i].useCapture) {
                this.removeEventListener(type, this._listeners[i].listener, useCapture);
            }
        }
    }
};

// Link 1, quando pressionado, muda a cor da fonte para vermelho.
document.getElementById("a1").addEventListener("click", function (event) {
    this.style.color = "red";
});

// Link 2, quando pressionado, muda a cor da fonte para azul, mas o evento é removido.
document.getElementById("a2").addEventListener("click", function (event) {
    this.style.color = "blue";
});

document.getElementById("a2").removeEventListeners("click");

// Link 3, quando pressionado, muda a cor da fonte para verde. O evento é definido como useCapture=true, mas é tentado remover com useCapture=false.
document.getElementById("a3").addEventListener("click", function (event) {
    this.style.color = "green";
}, true);

document.getElementById("a3").removeEventListeners("click");
<a href="#!" id="a1">Link 1</a>
<a href="#!" id="a2">Link 2</a>
<a href="#!" id="a3">Link 3</a>
  • % w /% keeps the event as added;
  • The Link 1 returns to the original behavior, because the listener is removed;
  • The Link 2 holds the event because it is added with Link 3 and attempted to remove with useCapture=true .

In this way, you can remove listeners even though they are defined as an anonymous function or an arrow function.

Summary of calls from useCapture=false :

// Remove os listeners do evento 'click', com useCapture=False:
element.removeEventListeners("click");

// Remove os listeners do evento 'click', com useCapture=True:
element.removeEventListeners("click", true);

// Remove todos os listeners, com useCapture=False:
element.removeEventListeners();

// Remove todos os listeners, com useCapture=True:
element.removeEventListeners(null, true);
    
28.09.2017 / 22:43