blur 和 focus 事件不支持事件捕获且不冒泡,因此无法通过事件代理在父元素监听;替代方案是使用冒泡的 focusin/focusout 事件,它们兼容性好且支持事件代理。

blur 和 focus 事件**不支持事件捕获模式**,也无法通过标准的事件代理(event delegation)在父元素上监听子元素的 blur/focus,因为这两个事件**默认不冒泡**(bubbling phase 为 false),且没有捕获阶段的行为。
为什么 focus/blur 不能用 capture 实现代理
根据 DOM 规范:
-
focus 和 blur 是非冒泡事件(
bubbles: false),因此不会触发父元素的事件监听器(无论 capture 还是 bubble 阶段); - 尽管你可以对它们调用
addEventListener("focus", handler, true),但浏览器会**忽略 capture 参数**,实际等同于false—— 即只在目标阶段触发,且仅限直接绑定的目标元素; - 尝试在父容器上监听子 input 的 focus,不会被触发,即使用了
true;
替代方案:focusin / focusout
现代浏览器支持两个**冒泡版焦点事件**:focusin 和 focusout。它们行为与 focus/blur 几乎一致,但关键区别是:它们会冒泡,因此可以用于事件代理。
-
focusin在元素获得焦点时触发(冒泡),相当于可代理的 focus; -
focusout在元素失去焦点时触发(冒泡),相当于可代理的 blur; - 它们兼容性良好(Chrome 5+, Firefox 52+, Edge, Safari 5.1+);
示例:在表单上代理所有 input 的焦点行为
立即学习“Java免费学习笔记(深入)”;
document.querySelector('form').addEventListener('focusin', (e) => {
if (e.target.matches('input, select, textarea')) {
e.target.classList.add('focused');
}
});
document.querySelector('form').addEventListener('focusout', (e) => {
if (e.target.matches('input, select, textarea')) {
e.target.classList.remove('focused');
}
});
注意 focusin/focusout 的特殊行为
这两个事件虽然冒泡,但有两点需留意:
- 它们**不支持 capture 阶段的“提前拦截”意义**——即使你写
addEventListener('focusin', h, true),它仍只在冒泡阶段触发(因为规范定义 focusin/focusout 只有冒泡阶段); - 它们会在焦点从一个子元素转移到另一个子元素时触发两次(如从 input1 → input2,先 focusout input1,再 focusin input2),这点和原生 focus/blur 一致;
- 它们能正确响应 tab 键、click、API(
.focus())等所有焦点来源;
如果必须用 focus/blur:只能直接绑定或 MutationObserver 辅助
若因兼容性或特殊需求坚持用 focus/blur:
- 无法代理,只能遍历动态添加的元素并单独绑定;
- 配合
MutationObserver监听新增表单控件,自动绑定; - 或者封装成函数,在创建元素后显式调用
el.addEventListener('focus', ...);
这牺牲了代理的简洁性,但确保行为精确可控。


















