Javascript 使用循环删除事件侦听器只删除最后一个元素';听众

Javascript 使用循环删除事件侦听器只删除最后一个元素';听众,javascript,events,removeeventlistener,Javascript,Events,Removeeventlistener,我在HTML集合中有3个元素集,并使用forEach循环将onclick侦听器附加到它们 [].forEach.call(signBoxes, (e, i) => { e.addEventListener("click", callSetSign = () => setSign(signs[i])); }); function setSign({ name, src }) { const sign = name; const Img = src; player1Ch

我在HTML集合中有3个元素集,并使用forEach循环将onclick侦听器附加到它们

[].forEach.call(signBoxes, (e, i) => {
  e.addEventListener("click", callSetSign = () => setSign(signs[i]));
});

function setSign({ name, src }) {
  const sign = name;
  const Img = src;
  player1Choice.src = Img;
  [].forEach.call(signBoxes, (e, i) => {
    e.removeEventListener("click", callSetSign);
  });
  socket.emit("self-choose-sign", sign);
}
添加监听器效果很好,但是当我尝试以相同的方式删除它们时,只删除最后一个元素的监听器。如果我像这样修改函数,我会得到相同的结果

function setSign({ name, src }) {
  const sign = name;
  const Img = src;
  player1Choice.src = Img;
  signBoxes[0].removeEventListener('click', callSetSign)
  signBoxes[1].removeEventListener('click', callSetSign)
  signBoxes[2].removeEventListener('click', callSetSign) // only this one works
  socket.emit("self-choose-sign", sign);
}

有人能解释一下吗?

您可以使用元素的
数据集
对象将
字符串
类型的参数传递给事件处理程序

显然,尝试在函数对象上放置参数只会重写相同名称的属性,并且
.bind
方法不会返回调用它的函数。尝试在闭包中传递参数或调用
bind
将导致多个处理函数对象

数据集
方法的一个未经测试的示例:

[].forEach.call(signBoxes, (e, i) => {
  e.dataset.index = i;  // store index on element
  e.addEventListener("click", setSign);
});

function setSign() {
  const { name, src } = signs[this.dataset.index];; // lookup using index
  const sign = name;
  const Img = src;
  player1Choice.src = Img;
  [].forEach.call(signBoxes, (e, i) => {
    e.removeEventListener("click", setSign);
  });
  socket.emit("self-choose-sign", sign);
}

您可以使用元素的
dataset
对象将
string
类型的参数传递给事件处理程序

显然,尝试在函数对象上放置参数只会重写相同名称的属性,并且
.bind
方法不会返回调用它的函数。尝试在闭包中传递参数或调用
bind
将导致多个处理函数对象

数据集
方法的一个未经测试的示例:

[].forEach.call(signBoxes, (e, i) => {
  e.dataset.index = i;  // store index on element
  e.addEventListener("click", setSign);
});

function setSign() {
  const { name, src } = signs[this.dataset.index];; // lookup using index
  const sign = name;
  const Img = src;
  player1Choice.src = Img;
  [].forEach.call(signBoxes, (e, i) => {
    e.removeEventListener("click", setSign);
  });
  socket.emit("self-choose-sign", sign);
}

callSetSign=()=>setSign(signs[i])
非常感谢快速回答,但如果我在循环之前声明callSetSign,是否可以在不使用bind的情况下向它传递一个参数,以便以后可以删除侦听器?
函数callSetSign(){return setSign(this);}
e.addEventListener(“单击”,callSetSign)配对
我可以想象,它会允许你做你想做的事情
callSetSign=()=>setSign(signs[I])
感谢你的快速回答,但是如果我在循环之前声明callSetSign,是否可以不使用bind向它传递一个参数,这样我以后就可以删除监听器了?
函数callSetSign(){return setSign(this);}
e.addEventListener(“单击”,callSetSign)配对
我可以想象,如果
event.path[1].id
(DOM元素的id)是
someValue
,那么我就可以通过
setSign[correspondingValue]
,因为我只检查了3个元素。这对我列出很长的清单没有帮助。您的解决方案有效,而且更好。甚至不必使用dataset,因为它也可以使用
e.index
。@Zobla通常可以将
HtmleElement
对象视为普通JavaScript对象,并向其添加任何数据类型的新属性,从历史上看,这会导致问题,并被视为非标准。由于
数据集
属性是在标准中定义的,因此它在未来仍然是可预测的。我能够提出不令人满意的解决方案,因为如果
event.path[1].id
(DOM元素的id)是
someValue
,我只检查了3个元素,然后我将传递
setSign[correspondingValue]
。这对我列出很长的清单没有帮助。您的解决方案有效,而且更好。甚至不必使用dataset,因为它也可以使用
e.index
。@Zobla通常可以将
HtmleElement
对象视为普通JavaScript对象,并向其添加任何数据类型的新属性,从历史上看,这会导致问题,并被视为非标准。由于
数据集
属性是在标准中定义的,因此它在未来仍然是可预测的。