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/>`;     
  }
}

Tags:

Javascript