javascript - 为什么此代码使用 `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>
解决方案
目前,当您点击 时Space,仍然绑定所有事件侦听器(从按钮单击开始),并且每个回调按顺序执行。如果按下第一个按钮,则usekeyUp
绑定keyUp
. 击中Space执行keyUp
、解除绑定keyUp2
和keyUp3
警报"hello world"
。它不会解除绑定keyUp
。
如果您随后单击另一个按钮,例如第二个按钮,它还会绑定keyUp2
. 但是如果你点击Space,当前绑定的事件监听器按顺序是keyUp
和keyUp2
。首先,keyUp
执行、解除绑定 keyUp2
和keyUp3
. 现在,没有其他事件监听器被绑定,而是永远keyUp
保持绑定。
文档中有关于此行为的注释:
如果在处理事件时从 EventTarget 中删除 EventListener,则不会被当前操作触发。删除后注册的事件不会调用 EventListener。但是,它可以重新连接。
因此,removeEventListener
s “工作”,但从未达到正确的。
如果您想继续使用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" />
推荐阅读
- docker - 无法访问主机外部的 docker kibana
- python-2.7 - 是否可以一次从 RabbitMQ 队列中提取所有消息?
- android - 计时器计时器不会停止
- sql - 是否可以在审计表中有单独的列来存储列名以反映所做的更改
- java - 可选的通用参数或对抗类型错误的方法
- sql-server - mvc,两个项目连接到一个数据库,用项目-A更新表它在项目-B中不起作用
- airflow - MySqlToGoogleCloudStorageOperator 意外失败
- azure-cosmosdb - 是否可以使用 Azure cosmos mongodb API 创建多个“唯一且稀疏”的索引
- ios - iOS 12.0 Safari - 滚动时图像失真
- html - 时间选择器值未在顶部弹出