You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

添加!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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 10:48:14