ES6 Class - method call from event handler

I am trying to write an ES6 class for an interactive calendar in my current project.

The class looks something like this:

class Calendar { constructor (s) { this.eventButtons = s.eventButtons; this.eventButtons.forEach(button => button.addEventListener('click', this.method1); this.eventBoxes = s.eventBoxes; method1 (e) { e.preventDefault(); this.method2(e.target.href); } method2 (url) { console.log(url); } } export default Calendar; 

I know that the context of the 'this' keyword has changed from the constructor to the button that was pressed in the method1 function. However, I do not know how to save the context of the button and constructor within the same function. I tried changing the button event listener code to the following:

 this.eventButtons.forEach(button => button.addEventListener('click', this.method1).bind(this); 

But that just switches the context of the 'this' keyword to the constructor, not the button. I need to use both in my function.

Any ideas? I hope this is a fairly common problem?

+5
source share
6 answers

You can create a closure that will send an event and a button. Closing will maintain this context and also send a button

 button => button.addEventListener('click', event => this.method1(event, button)) 
+6
source

Since you are using ES6, have you tried using the function?

An arrow function expression has a shorter syntax than an expression function and does not bind its own, arguments, super, or new.target. These functional expressions are best suited for non-method functions, and they cannot be used as constructors.

 method1 = (e) => { e.preventDefault(); this.method2(e.target.href); } 
+5
source

You have several options:

You can bind the methods themselves:

 this.method1 = this.method1.bind(this); this.method2 = this.method2.bind(this); 

There bind the operator if you are using Babel (or some other transporter). It has not yet been accepted into the standard, so I would be tired of using it. Using the bind operator, you can make the equivalent:

 this.method1 = ::this.method1 this.method2 = ::this.method2 

Another option is to do what you have already done, just adjusted.

You must bind a method, not the result of forEach.

 this.eventButtons.forEach(button => button.addEventListener('click', this.method1.bind(this))); 

or with op binding:

 this.eventButtons.forEach(button => button.addEventListener('click', ::this.method1)); 

Finally, you can also create a wrapper function using arrow notation for the lexical region:

 this.eventButtons.forEach(button => button.addEventListener('click', (...params) => this.method1(...params))); 
+3
source

And if you use ES6, you can also use instead of forEach. This prevents another callback from being created with its own scope. In this code, the keyword 'this' still refers to the source class.

 this.eventButtons = s.eventButtons; for(b of this.eventButtons){ b.addEventListener('click', () => this.method1); } 
+3
source

You can use bind to create a partial function:

 this.eventButtons.forEach(button => button.addEventListener('click', this.method1.bind(this, button)); 

It works if you change method1 as follows:

 method1 (button, e) { e.preventDefault(); this.method2(e.target.href); } 
+2
source

Try using a lambda expression to set the delegate of your event. Something like below:

 button.addEventListener('click', (e) => { e.preventDefault(); this.method2(); }); 
+1
source

Source: https://habr.com/ru/post/1269602/


All Articles