focusin 和 focusout 事件天然支持冒泡,适合表单级焦点代理;原生 focus/blur 不冒泡,而 focusin/focusout 可穿透层级向上触发,且语义一致、能准确获取目标元素。

focusin 和 focusout 事件天然支持冒泡,这使得它们非常适合用于表单级的焦点代理——无需为每个输入框单独绑定事件,只需在表单容器上监听一次,就能统一响应所有子输入框的聚焦与失焦行为,进而实现整单高亮等交互效果。
为什么选 focusin/focusout 而不是 focus/blur
原生 focus 和 blur 事件不冒泡,无法在父元素上捕获子元素的焦点变化;而 focusin 和 focusout 是 W3C 标准中明确支持冒泡的对应事件,语义一致(focusin ≈ focus,focusout ≈ blur),且能穿透层级向上触发。
- focusin 在目标元素获得焦点时触发,并向父级冒泡
- focusout 在目标元素失去焦点时触发,并向父级冒泡
- 两者都可通过 event.target 准确获取实际获得/失去焦点的输入框
用事件代理实现表单整体高亮
给整个表单(如 <form> 或包裹容器)绑定 focusin/focusout,根据事件类型动态添加/移除高亮类名。关键点在于:只对可聚焦的表单控件(input、textarea、select、button 等)生效,避免误响应非表单元素。
- 监听 form 元素上的 focusin 和 focusout
- 在 focusin 中检查 event.target 是否为表单控件(例如:event.target.matches('input, textarea, select, [contenteditable]'))
- 满足条件则给 form 添加 .form-focused 类(CSS 中定义边框/阴影等高亮样式)
- 在 focusout 中延时判断:只有当焦点真正离开整个表单区域时才移除高亮(避免 tab 切换时短暂闪烁)
处理 focusout 的“真离开”判断
focusout 会因内部元素切换而频繁触发,直接移除高亮会导致 tab 导航时闪烁。需确认焦点是否移出了表单边界:
立即学习“Java免费学习笔记(深入)”;
- 使用 event.relatedTarget 获取即将获得焦点的元素
- 若 relatedTarget 为 null(如点击空白处、切到其他窗口)或不在当前 form 内,则视为真正离开
- 推荐写法:!form.contains(event.relatedTarget)
完整轻量示例代码
不依赖框架,纯 JS 实现:
const form = document.querySelector('form');
form.addEventListener('focusin', e => {
if (e.target.matches('input, textarea, select, button, [contenteditable]')) {
form.classList.add('form-focused');
}
});
form.addEventListener('focusout', e => {
// 延迟执行,确保 focus 流已稳定
setTimeout(() => {
if (!form.contains(document.activeElement)) {
form.classList.remove('form-focused');
}
}, 0);
});
CSS 配合(可选):
.form-focused {
outline: 2px solid #007bff;
outline-offset: 2px;
}


















