按下按钮的Enter键并聚焦到下一个文本框后,为何下一个文本框会触发keyup事件?
按下按钮的Enter键并聚焦到下一个文本框后,为何下一个文本框会触发keyup事件?
这个问题我之前也碰到过,其实是键盘事件的传播时机在搞鬼!
问题原因拆解:
当你在按钮上按下Enter键时,会按以下顺序触发事件:
- 按钮先接收到
keydown事件(此时Enter键处于按下状态) - 浏览器的默认行为:按钮的Enter按键会自动触发它的
click事件,你的click处理函数执行,立刻把焦点转移到了下一个输入框(item-2) - 当你松开Enter键时,此时焦点已经在输入框
item-2上了,所以这个输入框会捕获到keyup事件;刚好你的输入框绑定了keyup的Enter监听逻辑,于是又触发了跳转,导致连续跳过了两个元素。
几种靠谱的解决方案:
方案1:改用keydown事件处理输入框跳转
keydown是在按键按下瞬间触发的,此时焦点还停留在当前元素上,不会出现焦点转移后捕获同个按键事件的问题:
const selects = document.querySelectorAll('.item'); selects.forEach(el => el.addEventListener('keydown', e => { if (e.key === 'Enter' || e.keyCode === 13) { e.preventDefault(); // 阻止默认行为,避免额外的浏览器动作 let idx = parseInt(e.currentTarget.getAttribute("index")); focusToTheNextControl(idx); } }));
方案2:直接处理按钮的keyup事件,阻止默认click触发
按钮的Enter按键默认会触发click事件,我们可以直接接管按钮的键盘事件,避免后续的意外触发:
// 替换原来的按钮click监听 document.querySelector('.btn-item').addEventListener('keyup', e => { if (e.key === 'Enter' || e.keyCode === 13) { e.preventDefault(); // 阻止默认的click事件触发 let idx = parseInt(e.currentTarget.getAttribute("index")); focusToTheNextControl(idx); } });
这样按钮的Enter按键事件完全由我们处理,焦点转移后,松开Enter的事件已经被按钮处理完毕,输入框不会再接收到这个keyup。
方案3:在输入框的keyup事件中增加焦点校验
确保只有当输入框本身是当前焦点元素时,才执行跳转逻辑:
const selects = document.querySelectorAll('.item'); selects.forEach(el => el.addEventListener('keyup', e => { if ((e.key === 'Enter' || e.keyCode === 13) && document.activeElement === el) { let idx = parseInt(e.currentTarget.getAttribute("index")); focusToTheNextControl(idx); } }));
关于你用setTimeout有效的原因:
延迟1秒后,Enter键的keyup事件已经完全处理完成了,此时再聚焦输入框,自然不会触发它的keyup监听。但这只是临时的 workaround,上面的方案更稳定可靠。
备注:内容来源于stack exchange,提问作者Qudu Ta
相关产品推荐
相关产品推荐

