I have a very "simple" vanillaJS problem. How can I remove event listener in loop inside a currying function? This just an example of my current solution where I actually need multiple parameters coming to the listener. I suspect that the event listener is not removed due to anonymous callback if I am right? How can I fix this? Example here https://codepen.io/shnigi/pen/wvPwqVR
const setEventListener = (buttons) => (event) => {
const buttonValue = event.target.value;
console.log('Eventlistener exists', buttonValue)
buttons.forEach(button => button.removeEventListener('click', setEventListener));
};
const buttons = document.querySelectorAll('button');
buttons.forEach(button => button.addEventListener('click', setEventListener(buttons)));
// Works as expected, listener is named
const testbutton = document.getElementById('kek');
const testListener = () => {
console.log('I show up only once');
testbutton.removeEventListener('click', testListener);
};
testbutton.addEventListener('click', testListener);
<button value="1">press me</button>
<button value="2">press me</button>
<button id="kek">eventlistener removed on click</button>
I suspect that the event listener is not removed due to anonymous callback if I am right?
Yes, that is a problem. So you have to assigned anonymous function to variable and use it to remove event listener.
Here is sample code for you.
const setEventListener = (buttons: NodeList) => {
const returnedFunc = (event: Event) => {
const buttonValue = (event.target as HTMLButtonElement).value;
console.log('Eventlistener exists', buttonValue)
buttons.forEach(button => button.removeEventListener('click', returnedFunc));
};
return returnedFunc;
};
const buttons: Nodelist = document.querySelectorAll('button');
buttons.forEach(button => button.addEventListener('click', setEventListener(buttons)));
// Works as expected, listener is named
const testbutton = document.getElementById('kek');
const testListener = () => {
console.log('I show up only once');
testbutton.removeEventListener('click', testListener);
};
testbutton.addEventListener('click', testListener);
If you don't care about Internet Explorer support, you can remove anonymous event listeners in more elegant way using AbortController. Pass signal option to addEventListener() and call AbortController's abort() method when you want to remove event listener:
var listenersRemover = new AbortController();
button.addEventListener('click', callback, {signal: listenersRemover.signal}));
listenersRemover.abort();
Note: you should "rearm" abort controller to use the same signal for the new addEventListener() calls. Just initialize it again with new AbortController().
var listenersRemover = new AbortController();
const setEventListener = (buttons) => (event) => {
const buttonValue = event.target.value;
console.log('Eventlistener exists', buttonValue)
// buttons.forEach(button => button.removeEventListener('click', setEventListener));
};
const buttons = document.querySelectorAll('button');
buttons.forEach(button => button.addEventListener('click', setEventListener(buttons), {signal: listenersRemover.signal}));
// Works as expected, listener is named
const testbutton = document.getElementById('kek');
const testListener = () => {
console.log('I show up only once');
//testbutton.removeEventListener('click', testListener);
listenersRemover.abort();
};
testbutton.addEventListener('click', testListener, {signal: listenersRemover.signal});
<button value="1">press me</button>
<button value="2">press me</button>
<button id="kek">eventlistener removed on click</button>