为什么监听键盘快捷键时 preventDefault 不生效?
我在做一个富文本编辑器,想用 Ctrl+B 实现加粗功能。但按下 Ctrl+B 时浏览器还是会触发默认的“书签”行为(比如 Chrome 会打开书签栏),我明明已经调用了 preventDefault(),不知道是哪里写错了。
我试过在 keydown 事件里判断 e.ctrlKey 和 e.key === ‘b’,也调用了 e.preventDefault(),但没用。是不是事件监听的位置不对?或者需要阻止其他事件?
<div contenteditable="true" id="editor">输入内容...</div>
<script>
document.getElementById('editor').addEventListener('keydown', (e) => {
if (e.ctrlKey && e.key === 'b') {
e.preventDefault();
document.execCommand('bold', false, null);
}
});
</script>
记得加activeElement判断,不然会影响其他输入框。
你把事件加在了 contenteditable 的 div 上,但浏览器的快捷键行为(比如 Ctrl+B 打开书签栏)是在 document 或 window 层级触发的,不是在你那个 div 上。也就是说,当用户按下 Ctrl+B 时,浏览器先捕获了全局快捷键,再把 keydown 事件派发到当前焦点元素(也就是你的 div),但有些快捷键的默认行为根本不会等你到那个阶段——它们在更高层级就被拦截了。
正确做法是把监听器加到 document 上,或者更稳妥点,加到 window 上,而且要确保用捕获阶段(第三个参数设为 true),这样能比浏览器默认行为更早拦截。
我之前踩过这个坑,试过各种组合,最后发现只有在捕获阶段加到 window 上才稳:
另外,有些快捷键(比如 Ctrl+B、Ctrl+P、Ctrl+S)是浏览器保留的,即使 preventDefault 了,某些版本 Chrome 还是会弹出对话框,尤其是当焦点不在可编辑区域时。所以务必保证 editor 是 focus 状态,或者直接用 window 捕获阶段拦截。
最后提醒一句,现在 execCommand 基本算遗产 API 了,新项目尽量用 Selection + Range 自己控制,或者直接上 Slate、Lexical 这类成熟库,省得再掉进这种兼容性坑里。