Utilizing prototype methods as event handlers can pose challenges, especially when you require both the bound this value associated with the instance and a reference to the actual event handler function.
Typically, the event queue executes the handler within the context of the element to which the event was initially attached. While it's possible to alter this context, doing so necessitates creating a new function, thereby disconnecting it from the method in the prototype.
To maintain a concise class structure, one approach is to define the event handler methods as unique properties of the instance that cannot be inherited. An easy solution involves defining these methods as arrow functions directly within the constructor.
class Test {
constructor() {
this.eventHandler = e => {
console.log(e.target.id);
e.target.removeEventListener("click", this.eventHandler);
};
let b = document.getElementById("b");
b.addEventListener("click", this.eventHandler);
}
}
new Test();
<button id="b">Click me!</button>
The arrow function retains the lexical environment in which it was defined, preventing the event queue from overriding the context. This ensures that the this keyword in the handler function is correctly linked to the instance, while maintaining a reference to the attached event handler function.
An alternative approach, slightly less memory-intensive, involves using bind when defining the custom property:
class Test {
constructor() {
this.eventHandler = this.eventHandler.bind(this);
let b = document.getElementById("b");
b.addEventListener("click", this.eventHandler);
}
eventHandler (e) {
console.log(e.target.id);
e.target.removeEventListener("click", this.eventHandler);
}
}
In this scenario, bind generates a new function object that points to the method in the prototype without duplicating its code. This technique is similar to explicitly calling the prototype method:
this.eventHandler = e => Test.prototype.eventHandler.call(this, e);
It's important to note that shadowing an underlying prototype property with a similarly named own property within the instance will not override the prototype property; instead, it will simply coexist alongside it, ensuring multiple instances of the class continue to function correctly.
Another option involves developing your own "event model" that encapsulates wrapper functions for all events, mimicking the behavior seen in the previous code example. These wrappers utilize call to bind the desired this value to the event handler, and stored function references are employed for removing events. Constructing such a model offers insight into how this binding operates and enhances understanding of the native event model.