Ilyes Ferchiou
Ilyes Ferchiou

Reputation: 583

Remove nested event listeners in JavaScript classes

I have an event listener that is added by another event listener that is added by a function inside a class. I want this event listener to execute once and then remove itself.

To simplify the matter, let's assume I have the following HTML code :

<div class="container">
<div class="redDiv">some html</div>
<div class="blueDiv">some html</div>
</div>

and the following JavaScript code :

class Test {

    constructor(_container){
      this.container = _container;
    }

    somefunc(){
      // some code here
      this.removeEventListener('click',somefunc);
    }

    start(){
      var container = this.container;    
      container.querySelector('.redDiv').addEventListener('click',function(){
        container.querySelector('.blueDiv').addEventListener('click',somefunc);
      });
    } 
  }



let tester = new Test(document.querySelector('.container'));
tester.start();

The first problem I came across, is that somefunc is not defined at the scope of the second Event Listener. The only solution I have found was to create a new variable fnc in the start function and assign somefunc to it and then call fnc instead of somefunc.

This made adding the event listener work and the code run as expected but then when somefunc() was executed, removing the event listener didn't work because somefunc was undefined at this scope.

I tried to pass somefunc to this scope by changing the code to this :

class Test {

    constructor(_container){
      this.container = _container;
    }

    somefunc(func){
      return function(){
      // some code here
      this.removeEventListener('click',func);
      }
    }


    start(){
      var fnc = this.somefunc;
      var container = this.container;    
      container.querySelector('.redDiv').addEventListener('click',function(){
        container.querySelector('.blueDiv').addEventListener('click',fnc(fnc));
      });
    } 
  }

let tester = new Test(document.querySelector('.container'));
tester.start();

Now, the code doesn't generate any errors but the event listener isn't removed as desired. This has probably something to do with the way JavaScript assigns variables, a subject I don't really understand well. But what I'm interested in is how I can solve this matter and be able to remove the event listener once it is executed.

Upvotes: 0

Views: 612

Answers (1)

Sam Herrmann
Sam Herrmann

Reputation: 6986

One solution is to use an arrow function to maintain the reference of this...

class Test {

    constructor(_container){
      this.container = _container;
    }

    somefunc(fn){
      console.log('clicked blue div')
      this.container.querySelector('.blueDiv').removeEventListener('click', fn);
    }


    start(){
      var container = this.container;    
      container.querySelector('.redDiv').addEventListener('click',() => {
        console.log('clicked red div')
        // Wrap someFunc in an arrow function to have "this" inside somefunc 
        // reference this class. 
        const func = () => this.somefunc(func);
        container.querySelector('.blueDiv').addEventListener('click', func);
      });
    } 
  }

let tester = new Test(document.querySelector('.container'));
tester.start();

StackBlitz Example

Upvotes: 1

Related Questions