添加!e.repeat判断后keydown键盘事件仍重复触发两次的问题
问题根源
e.repeat属性仅能过滤操作系统在用户长按按键时自动派发的重复keydown事件,无法覆盖以下会导致单次按键触发多次非重复keydown的场景:
- 中文输入法、第三方键盘工具在按键触发时会额外派发独立的keydown事件,这类事件的
repeat属性同样为false - 页面焦点切换、事件冒泡被异常拦截重发时,会生成独立的非重复按键事件
- 部分消费级键盘硬件存在触点抖动问题,单次按动会向系统发送两次电信号,被识别为两次独立按键
修复方案
自行维护一个记录当前处于按下状态的按键集合作为状态锁,仅当按键不在已按下集合中时才触发播放逻辑,按键抬起时再将对应按键从集合中移除,从逻辑层面彻底阻断重复触发。
修改后的键盘事件处理代码如下:
// 新增:存储当前已按下的按键,作为重复触发拦截锁 const pressedKeys = new Set(); document.addEventListener("keydown", function(e) { // 按键已经处于按下状态,直接跳过不处理 if (pressedKeys.has(e.key)) return; if (!e.repeat && isValidInput(e.key)) { // 标记按键为已按下 pressedKeys.add(e.key); const pitch = keysMap.get(e.key); playNote(pitch); document.getElementById(pitch).classList.add("key-bkg-color"); } }) document.addEventListener("keyup", function(e) { // 非有效钢琴按键直接跳过,避免后续DOM查询报错 if (!isValidInput(e.key)) return; const pitch = keysMap.get(e.key); // 抬起后移除按键标记,允许下次按下触发 pressedKeys.delete(e.key); document.getElementById(pitch).classList.remove("key-bkg-color"); })
可选代码优化建议
- 原代码的keyup事件未做输入合法性校验,按下非映射按键时
keysMap.get(e.key)会返回undefined,后续执行document.getElementById(undefined)会抛出控制台错误,上述修复代码已经补上了该校验。 - 预加载音频的逻辑建议增加错误捕获,避免个别音频资源加载失败时阻塞其他资源加载,同时可以增加加载完成状态提示,避免用户在资源未加载完成时按键无响应。
document.getElementsByClassName返回的是动态DOM集合,遍历过程中如果DOM结构变化可能导致遍历逻辑异常,建议替换为document.querySelectorAll('.note')获取静态NodeList,稳定性更高。- 目前
playNote函数每次执行都会新建BufferSource和GainNode节点,长时间使用会存在内存泄漏风险,可以在音频播放结束后主动调用节点的disconnect()方法释放资源。
内容的提问来源于stack exchange,提问作者bookthief2468
相关产品推荐
相关产品推荐

