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);