focus 和 blur 事件不会冒泡,其事件对象的 bubbles 属性为 false;而 focusin/focusout 事件支持冒泡,可用于替代实现冒泡需求。
focus 和 blur 事件在原生 dom 中确实**不会冒泡**,这是规范明确规定的。你可以用一段简洁的 js 代码直接验证这一点,无需依赖框架或 polyfill。
用嵌套元素监听事件并触发 focus
创建一个父容器和一个可聚焦的子元素(如 input),分别给它们绑定 focus 和 blur 监听器,并设置 { capture: false }(默认):
const parent = document.getElementById('parent');
const input = document.getElementById('input');
<p>parent.addEventListener('focus', () => console.log('parent received focus'));
input.addEventListener('focus', () => console.log('input received focus'));</p><p>input.focus(); // 手动聚焦 input
</p>执行后,控制台**只输出 input received focus** —— 父元素的监听器完全没触发,说明 focus 没有向上冒泡。
检查事件对象的 bubbles 属性
更直接的方式是查规范属性。在任意 focus/blur 回调中打印事件对象:
input.addEventListener('focus', (e) => {
console.log('focus bubbles:', e.bubbles); // false
});
input.addEventListener('blur', (e) => {
console.log('blur bubbles:', e.bubbles); // false
});
两者都输出 false,符合 DOM 规范中对 focus/blur 的定义:它们是「non-bubbling」事件。
对比可冒泡的事件(如 input 或 keydown)
为了凸显差异,可以加一个同级的 input 事件监听:
input.addEventListener('input', () => console.log('input event bubbled!'));
parent.addEventListener('input', () => console.log('parent caught input!')); // 这行会执行
输入内容时,两行日志都会出现;但换成 focus,父级监听器永远静默 —— 这不是 bug,是设计如此。
注意:focusin/focusout 是例外
如果你需要冒泡版的聚焦行为,浏览器提供了 focusin 和 focusout 事件,它们**专门支持冒泡**,且兼容性良好(IE9+,现代浏览器全支持):
parent.addEventListener('focusin', () => console.log('focusin bubbled to parent'));
input.focus(); // 此时会触发上面那句 log
所以别试图让 focus/blur 冒泡,也不用阻止它的冒泡(因为它压根不冒泡)。要用冒泡语义,就换用 focusin/focusout。











