首页 > 解决方案 > 为什么此代码使用 `removeEventListener` 似乎无法正确解除绑定关键事件?

问题描述

我希望我的代码做什么

当我单击第一个按钮然后按下Space键时,它应该 alert "hello world"。当我单击第二个按钮然后按下Space键时,我希望它会发出警报"hello"。对第三个按钮重复此操作,它应该 alert "hello3"

代码的当前结果是什么

当我单击第一个按钮时,它起作用了:它会发出警报"hello world"。但问题是当我单击第二个或第三个按钮时:它仍然会发出警报"hello world"

我的代码有什么问题?

function usekeyUp() {
  document.addEventListener("keyup", keyUp);
}

function keyUp(event) {
  document.removeEventListener("keyup", keyUp2);
  document.removeEventListener("keyup", keyUp3);
  
  if (event.code === 'Space') {
    alert("hello world")
  }
}

function usekeyUp2() {
  document.addEventListener("keyup", keyUp2);
}

function keyUp2(event) {
  document.removeEventListener("keyup", keyUp);
  document.removeEventListener("keyup", keyUp3);
  
  if (event.code === 'Space') {
    alert("hello")
  }
}

function usekeyUp3() {
  document.addEventListener("keyup", keyUp3);
}

function keyUp3(event) {
  document.removeEventListener("keyup", keyUp);
  document.removeEventListener("keyup", keyUp2);
  
  if (event.code === 'Space') {
    alert("hello3")
  }
}
<button onclick="usekeyUp()">click</button>
<button onclick="usekeyUp2()">click</button>
<button onclick="usekeyUp3()">click</button>

标签: javascripthtmlremoveeventlistener

解决方案


目前,当您点击 时Space,仍然绑定所有事件侦听器(从按钮单击开始),并且每个回调按顺序执行。如果按下第一个按钮,则usekeyUp绑定keyUp. 击中Space执行keyUp、解除绑定keyUp2keyUp3警报"hello world"。它不会解除绑定keyUp

如果您随后单击另一个按钮,例如第二个按钮,它还会绑定keyUp2. 但是如果你点击Space,当前绑定的事件监听器按顺序是keyUpkeyUp2。首先,keyUp执行、解除绑定 keyUp2keyUp3. 现在,没有其他事件监听器被绑定,而是永远keyUp 保持绑定。

文档中有关于此行为的注释:

如果在处理事件时从 EventTarget 中删除 EventListener,则不会被当前操作触发。删除后注册的事件不会调用 EventListener。但是,它可以重新连接。

因此,removeEventListeners “工作”,但从未达到正确的。

如果您想继续使用removeEventListener,则将这些removeEventListener行移动到相应的click事件中,而不是keyup事件中。

不推荐使用内联事件处理onclick程序。它们是一种过时、难以维护且不直观的事件记录方式。始终使用addEventListener

document.getElementById("click1").addEventListener("click", function(){
  document.addEventListener("keyup", keyUp1);
  document.removeEventListener("keyup", keyUp2);
  document.removeEventListener("keyup", keyUp3);
});
document.getElementById("click2").addEventListener("click", function(){
  document.removeEventListener("keyup", keyUp1);
  document.addEventListener("keyup", keyUp2);
  document.removeEventListener("keyup", keyUp3);
});
document.getElementById("click3").addEventListener("click", function(){
  document.removeEventListener("keyup", keyUp1);
  document.removeEventListener("keyup", keyUp2);
  document.addEventListener("keyup", keyUp3);
});

function keyUp1(event){
  if (event.code === "Space") {
    alert("hello world")
  }
}

function keyUp2(event){
  if (event.code === "Space") {
    alert("hello")
  }
}

function keyUp3(event){
  if (event.code === "Space") {
    alert("hello3")
  }
}
<input id="click1" type="button" value="First"/>
<input id="click2" type="button" value="Second"/>
<input id="click3" type="button" value="Third"/>

由于这段代码非常WET,更好的方法是使用事件委托而不是分配多个事件——它更易于维护,并且适用于动态添加的元素。例如,使用事件参数target. 查看标签信息什么是 DOM 事件委托?.

{
  let currentAlert;

  addEventListener("keyup", ({code}) => {
    if (currentAlert && code === "Space") {
      alert(currentAlert);
    }
  });

  addEventListener("click", ({target}) => {
    if(target?.dataset?.alert){
      currentAlert = target.dataset.alert;
    }
  });
}
<input data-alert="hello world" type="button" value="First" />
<input data-alert="hello" type="button" value="Second" />
<input data-alert="hello3" type="button" value="Third" />


推荐阅读