Is it possible to call a class method with addEventListener?
No, what you've written wouldn't work, in that method
would be invoked without object
as its context. Inside method
, this
would be set to the DOM element which initiated the event.
If you want to invoke the method and retain the context, close over the object
variable with a function:
var object = new ClassName();
document.getElementById('x').addEventListener('click', function () {
object.method()
}, false);
Yes is possible by doing a binding.
Below, I'm creating a Web Component. A feature that is supported in most browsers.
To create this Web Component, I'm creating a class that extends the HTMLElement
class. As far as JavaScript goes, this is just another regular class.
Now, I want my Web Component to behave like a button. I want a counter to be increased each time my tag gets click on.
Note: The three dots is an indication that "more code" goes there.
So, I create a variable that will hold the count inside the constructor:
constructor(){
...
this.count = 0;
...
Then, I add a listener that detects when my Web Component is click on and call the method onClick
:
constructor(){
...
this.count = 0;
...
this.addEventListener('click', this.onClick, false);
}
onClick(event){
let prevCount = this.count;
this.count++;
let msg = `Increased Count from ${prevCount} to ${this.count}`;
document.getElementById("log").innerHTML += `${msg}<br/>`;
}
The onClick is designed to increase the counter and print the previous and current value in the counter; however, there is a problem here!!!
The problem is in this line this.count++;
. The keyword this
isn't referring to the instance of the class HTMLExample
. The reason is that functions added with addEventListener
will reference the bound element as this
, not the function or object.
Therefore, you must do a binding of that function, used by the addEventListener
to the instance of your class by adding this line of code:
this.onClick = this.onClick.bind(this);
Now, when you use the this
keyword inside the method onClick
, the this
will be referring to the instance of the class HTMLExample
.
Please check the code below and run it. I think it will become clear that way:
class HTMLExample extends HTMLElement{
constructor(){
super();
this.count = 0;
this.onClick = this.onClick.bind(this);
this.addEventListener('click', this.onClick, false);
}
onClick(event){
let prevCount = this.count;
this.count++;
let msg = `Increased Count from ${prevCount} to ${this.count}`;
document.getElementById("log").innerHTML += `${msg}<br/>`;
}
}
customElements.define('example-component', HTMLExample);
example-component{
cursor: pointer;
border: 1px solid black;
background: lightgray;
padding: 2px;
}
<example-component>Button</example-component>
<div id="log"></div>
I just tried a more direct approach, it appears to work and I'd say it's pretty clean. I just add bind(this)
to the argument to addEventListener()
. Don't make it complicated when it's not...
class HTMLExample extends HTMLElement{
constructor(){
super();
this.count = 0;
this.addEventListener('click', this.onClick.bind(this), false);
}
onClick(event){
let prevCount = this.count;
this.count++;
let msg = `Increased Count from ${prevCount} to ${this.count}`;
document.getElementById("log").innerHTML += `${msg}<br/>`;
}
}