0

I'm trying to remove click events from a list of id's after adding them with an IIFE like this

function setupPlayer(player){
  var squareState = {};
  for (i = 0; i < allSquares.length; i++) {
      if(allSquares[i].innerHTML === "") {
        // set up a click event for each square
         document.getElementById(allSquares[i].getAttribute('id')).addEventListener('click', (clickSquare)(i));
      }
    }
}

The clickSquare function returns

function clickSquare(i){
  var num = i;
  return function() {
      document.getElementById(allSquares[num].getAttribute('id')).innerHTML=player;
  }
}

Then I try to remove them with

function removeClickEvents(){
  for (let i = 0; i < allSquares.length; i++) {
    document.getElementById(allSquares[i].getAttribute('id')).removeEventListener('click', clickSquare);
  }
} 

I've tried naming the returned anonymous function and using removeEventListener on that to no avail.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
balcoder
  • 719
  • 1
  • 8
  • 14
  • You either remove all (isn't there an API function to remove _all_ events listeners?), or you need to keep a pointer to the actual function. Sending in the function.creating function is useless - that's an entirely different pointer (to a different function object). There is no magic or crystal ball:The event system simply stores all callback function pointers in a structure and when you remove one tries to find the one you send in for removal in that structure. Also seee https://stackoverflow.com/a/4386514/544779 – Mörre Oct 01 '17 at 17:28
  • What are the elements of `allSquares` array? Are there elements with duplicate `id` within the `document`? What is purpose of `document.getElementById(allSquares[i].getAttribute('id'))`? – guest271314 Oct 01 '17 at 17:38

2 Answers2

4

To remove event listener from a DOM element you need to pass the same function you used while adding event listener, as the parameter.

In javascript when you create an object it creates a new instance of that object class, so it won't be equal to another object even if it is created with same parameters

Example:

{} != {} // returns true
[] != [] // returns true

Same goes with function, whenever you write function (){} it creates a new instance of Function class.

Example:

function a() {
    return function b() {}
}
a() != a() // returns true

Solution:

So for you to be able to remove the event listeners, you will have to store the functions you have passed to addEventListener

var listeners = [];

function setupPlayer(player) {
    var squareState = {};
    for (i = 0; i < allSquares.length; i++) {
        if(allSquares[i].innerHTML === "") {
            listeners[i] = clickSquare(i);
            document.getElementById(allSquares[i].getAttribute('id')).addEventListener('click', listeners[i]);
        }
    }
}

function clickSquare(i) {
    var num = i;
    return function() {
        document.getElementById(allSquares[num].getAttribute('id')).innerHTML=player;
    }
}

function removeClickEvents() {
    for (let i = 0; i < allSquares.length; i++) {
        if(listeners[i]) {
            document.getElementById(allSquares[i].getAttribute('id')).removeEventListener('click', listeners[i]);
        }
    }
}

From your code where you are using

document.getElementById(allSquares[i].getAttribute('id'))

I am assuming that allSquares[i] is a DOM element already, your code can be more simplified

var listeners = [];

function setupPlayer(player) {
    var squareState = {};
    for (i = 0; i < allSquares.length; i++) {
        if(allSquares[i].innerHTML === "") {
            listeners[i] = clickSquare(i);
            allSquares[i].addEventListener('click', listeners[i]);
        }
    }
}

function clickSquare(i) {
    var num = i;
    return function() {
        allSquares[num].innerHTML=player;
    }
}

function removeClickEvents() {
    for (let i = 0; i < allSquares.length; i++) {
        if(listeners[i]) {
            allSquares[i].removeEventListener('click', listeners[i]);
        }
    }
}
AvcS
  • 2,263
  • 11
  • 18
0

The function is being called immediately at (clickSquare)(i). At code at Question allSquares appears to be the element itself, clickSquare function can be referenced directly and event.target can be used within event handler to reference the current element in allSquares collection

let player = 123;

setInterval(() => player = Math.random(), 1000);

onload = () => {

  let allSquares = document.querySelectorAll("div[id|=square]");


  let button = document.querySelector("button");

  button.onclick = removeClickEvents;

  function setupPlayer(player) {
    var squareState = {};
    for (let i = 0; i < allSquares.length; i++) {
      if (allSquares[i].innerHTML === "click") {
        // set up a click event for each square
        allSquares[i].addEventListener('click', clickSquare);
      }
    }
  }

  function clickSquare(event) {
    console.log(event.target);
    event.target.innerHTML = player;
  }

  function removeClickEvents() {
    for (let i = 0; i < allSquares.length; i++) {
      allSquares[i].removeEventListener('click', clickSquare);
    }
  }



  setupPlayer(player);

}
<div id="square-0">click</div>
<div id="square-1">click</div>
<div id="square-2">click</div>

<button>remove events</button>
guest271314
  • 1
  • 15
  • 104
  • 177