0

How do I properly remove an event listener...

function createMaze() {
    var x;  
    for (x = 0; x < 4; x++) { 
        var mazeBlock = document.createElement('div');
        document.body.appendChild(mazeBlock);
        mazeBlock.setAttribute('class', 'blockStyle');
        mazeBlock.setAttribute('id', 'mazeBlock'+x);
        mazeBlock.addEventListener( 'click', function(){ eventCall(this) } );   
    }
}

function eventCall(t) {
    alert( t.id );
    t.removeEventListener(); //...know that I'm missing something here.
// Also in my code, this remove will not happen here but be initiated somewhere else in the script.

}

I did bunch of digging and the top answer there suggest to add the listener to an object for easier removal but... I'm not sure how to accomplish that

Fuzzy
  • 1
  • 1

2 Answers2

3

While you could save a reference to the function you call addEventListener with so you can remove it:

for (let x = 0; x < 4; x++) {
  const mazeBlock = document.createElement('div');
  document.body.appendChild(mazeBlock);
  mazeBlock.className = 'blockStyle';
  mazeBlock.id = 'mazeBlock' + x;
  mazeBlock.addEventListener('click', function handler() {
    mazeBlock.removeEventListener('click', handler);
    eventCall(mazeBlock);
  });
}

(above, eventCall is called with the <div> as the first argument)

It would be easier to make sure the function can only be called once by passing { once: true } as a third argument to addEventListener:

mazeBlock.addEventListener( 'click', eventCall, { once: true });

(above, eventCall is called with the event as the first argument - to get to the <div>, access the .target of the argument)

If you need to remove listeners for all such elements, you might consider a different approach - rather than attaching lots of listeners and then removing them all, use event delegation instead. That way, all you have to do is remove the single delegated listener:

document.body.addEventListener('click', function handler(event) {
  if (!event.target.matches('.blockStyle')) return;
  // A block was clicked on, remove the listener:
  document.body.removeEventListener('click', handler);
  // Do stuff with the clicked element:
  eventCall(event.target);
});

If you're forced by weird school rules to add listeners to each element, create the listener function outside the loop, then iterate over all elements and remove the listener from each when needed:

const handler = (event) => {
  document.querySelectorAll('.blockStyle').forEach((div) => {
    div.removeEventListener('click', handler);
  });
  // do stuff with event and event.target
};
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • Thanks. It does work when the `removeEventListener` is within the `handler()` but I need to apply this in bulk from another location in the script, finding all elements by className 'blockStyle' and removing their respective event listeners – Fuzzy May 16 '20 at 19:57
  • You need to remove the listeners on *all* elements with that class name, is that it? – CertainPerformance May 16 '20 at 19:58
  • Yes. I'm doing `getElementsByClassName('blockStyle')` and looping through that array with `i.removeEventListener('click', handler)` but with no effect.. – Fuzzy May 16 '20 at 20:39
  • Each element has a different `handler`, so that method doesn't work - use event delegation instead, as shown in the answer – CertainPerformance May 16 '20 at 20:55
  • Thank you. Have to stick to certain procedure here (school project). – Fuzzy May 16 '20 at 20:57
0

...ended up doing this:

function createMaze() {
    var x;  
    for (x = 0; x < 4; x++) { 
        const mazeBlock = document.createElement('div');
        document.body.appendChild(mazeBlock);
        mazeBlock.className = 'blockStyle';
        mazeBlock.id = 'mazeBlock' + x;
        mazeBlock.addEventListener( 'click', eventCall );
    }
}

function eventCall() {
    alert( this.id );
}

//...this is called from another piece of the script on a separate occasion
function removeListeners() {
    var blocks = document.getElementsByClassName('blockStyle');
    for (var i = 0; i < blocks.length; i++) {
        var block = blocks[i];
        block.removeEventListener( 'click', eventCall );
    }
}

@CertainPerformance Thanks for all your help! :)

Fuzzy
  • 1
  • 1