Chrome中释放S键后重复触发D键keydown事件的原因及排查方法
问题解答:Chrome中释放S键时触发D键keydown的异常行为
一、为什么会出现这个现象?
这是Chrome(包括Chromium)在Linux平台下特有的键盘事件处理行为,和Linux桌面环境的键盘输入机制以及Chrome的事件适配逻辑有关。
当你按住多个按键(比如先按S再按D,同时按住两者),释放其中一个按键(S)时,Chrome会给当前仍处于按住状态的按键(D)补发一个keydown事件——但这个事件并不是物理上再次按下D键导致的,而是浏览器内部的事件补发逻辑。
Firefox没有这个问题,因为它的键盘事件处理更严格遵循W3C的键盘事件规范,只有物理按键按下时才会触发keydown。
二、如何判断第二次D键的keydown是由其他按键的up事件导致的?
你可以通过以下两种方式来检测这种异常触发的keydown事件:
- 维护当前按住的按键集合
记录所有处于按下状态的按键,当触发keydown时,如果该按键已经在集合中,并且最近的事件是其他按键的keyup,则说明这是异常触发的事件:
const pressedKeys = new Set(); let lastEvent = null; function handler(event) { if (!event.repeat) { if (event.type === 'keydown') { if (pressedKeys.has(event.code)) { // 检测到异常的keydown:按键已经处于按下状态 if (lastEvent?.type === 'keyup' && lastEvent.code !== event.code) { console.log(`${event.code} keydown - 由${lastEvent.code}的keyup触发`); } } else { console.log(`${event.code} ${event.type} - 正常物理按下`); pressedKeys.add(event.code); } } else if (event.type === 'keyup') { console.log(`${event.code} ${event.type} - 释放`); pressedKeys.delete(event.code); } } lastEvent = event; } window.addEventListener('keydown', handler); window.addEventListener('keyup', handler);
- 对比事件时间戳
异常触发的keydown和之前的keyup事件时间差会极小(通常在几毫秒内),可以通过时间戳来判断:
let lastUpTimestamp = 0; function handler(event) { if (!event.repeat) { if (event.type === 'keydown') { if (event.timeStamp - lastUpTimestamp < 10) { console.log(`${event.code} ${event.type} - 由其他按键的keyup触发`); } else { console.log(`${event.code} ${event.type} - 正常物理按下`); } } else if (event.type === 'keyup') { console.log(`${event.code} ${event.type} - 释放`); lastUpTimestamp = event.timeStamp; } } } window.addEventListener('keydown', handler); window.addEventListener('keyup', handler);
三、这是Bug还是预期行为?
这属于Chrome在Linux平台的非标准行为,并不符合W3C键盘事件规范的预期(规范中keydown事件应仅在物理按键被按下时触发)。你可以把它看作是Chrome为了适配Linux桌面环境的键盘输入逻辑而产生的兼容性问题,在Firefox等其他浏览器中不会出现。目前Chrome的bug tracker上也有类似的问题记录,但暂时没有官方修复的时间表。
内容的提问来源于stack exchange,提问作者Simon
相关产品推荐
相关产品推荐

