crowhill
crowhill

Reputation: 2548

Add an event listener with parameters and then remove it

The basic scenario

This question has been asked before. In fact, it's been asked with nearly the same title but please stick with me for a moment, I don't think the question has a good answer yet.

So, we have an event listener:

fancyButton.addEventListener('click', this.launchKitty, true);

And then we have a function:

// assume this.setForLaunch was set somewhere
launchKitty = () => {
  if(this.setForLaunch){ // behold, this!
    console.log('farewell, feline!')
  }
}

We have an event listener and a function with access to the outer this, which is neat. Removal is easy:

fancyButton.removeEventListener('click', this.launchKitty, true);

But what if we didn't want to use this?

Parameters!

What if, instead of leaning on this, we wanted to pass addEventListener an argument? Like so:

fancyButton.addEventListener('click', () => {this.launchKitty(true)}, true);

And a slightly modified launchKitty():

launchKitty(setForLaunch) {
  if(setForLaunch){ // now we're using a parameter!
    console.log('farewell, feline!')
  }
}

So far, so good. Kitties are launched. What about removing the listener?

fancyButton.removeEventListener('click', () => {this.launchKitty}, true);

Not going to work. Our () => {} is anonymous and therefore removeEventListener doesn't know how to find it.

The question

How can we pass an event listener a parameter and then later remove said event handler?

Please feel free to challenge any of my assumptions. There are a lot of ways to launch this cat.

Thanks!

Upvotes: 2

Views: 1649

Answers (2)

vicatcu
vicatcu

Reputation: 5837

Alternatively to @CertainPerformance's answer. You can use the bind method from the Function prototype to instantiate a 'copy' of the function with parameters bound to it.

Something like:

function launchKitty(setForLaunch) {
  console.log(`launchKitty called with setForLaunch = ${setForLaunch}`);
}

let fancyTrue = launchKitty.bind(null, true);
let fancyFalse = launchKitty.bind(null, false);

let fancyButton = document.getElementById('fancyButton');
let lessFancyButton = document.getElementById('lessFancyButton');
let detachButton =  document.getElementById('detachFancyButton');

fancyButton.addEventListener('click', fancyTrue, true);

lessFancyButton.addEventListener('click', fancyFalse, true);


detachButton.addEventListener('click', () => {
  fancyButton.removeEventListener('click', fancyTrue, true);
  lessFancyButton.removeEventListener('click', fancyFalse, true);
});
<button id='fancyButton'>Console Log</button>
<button id='lessFancyButton'>No Console Log</button>

<button id='detachFancyButton'>Detach Handlers</button>

Upvotes: 1

CertainPerformance
CertainPerformance

Reputation: 370699

You should store the currently anonymous function into a variable, so that you can call removeEventListener with it later:

const launchKittyTrue = () => { this.launchKitty(true) };
fancyButton.addEventListener('click', launchKittyTrue, true);
// later:
fancyButton.removeEventListener('click', launchKittyTrue, true);

To be more flexible, you might consider a Map indexed by arguments, whose values are the bound functions:

const boundFns = new Map();
function makeListener(arg) {
  if (!boundFns.has(arg)) {
    boundFns.set(arg, launchKitty.bind(undefined, arg));
  }
  return boundFns.get(arg);
}
function getListener(arg) {
  return boundFns.get(arg);
}
fancyButton.addEventListener('click', makeListener(true), true);
// later:
fancyButton.removeEventListener('click', getListener(true), true);

Upvotes: 4

Related Questions