不会冒泡的事件主要有以下几类:
1. 焦点相关事件
focus- 元素获得焦点blur- 元素失去焦点focusin- 元素获得焦点(会冒泡)focusout- 元素失去焦点(会冒泡)
// focus 和 blur 不会冒泡
document.getElementById('input').addEventListener('focus', (e) => {
console.log('focus 事件不会冒泡到父元素');
}, false); // 捕获阶段监听
// 使用 focusin 替代(会冒泡)
document.getElementById('parent').addEventListener('focusin', (e) => {
console.log('focusin 事件会冒泡');
});2. 鼠标进入/离开事件
mouseenter- 鼠标进入元素mouseleave- 鼠标离开元素mouseover- 鼠标进入元素(会冒泡)mouseout- 鼠标离开元素(会冒泡)
复制代码
<div id="outer" style="padding: 50px; background: lightblue;">
<div id="inner" style="padding: 30px; background: lightcoral;"></div>
</div>
<script>
const outer = document.getElementById('outer');
const inner = document.getElementById('inner');
// mouseenter/mouseleave 不会冒泡
outer.addEventListener('mouseenter', () => console.log('outer: mouseenter'));
outer.addEventListener('mouseleave', () => console.log('outer: mouseleave'));
// mouseover/mouseout 会冒泡
outer.addEventListener('mouseover', () => console.log('outer: mouseover - 会冒泡'));
outer.addEventListener('mouseout', () => console.log('outer: mouseout - 会冒泡'));
</script>3. 加载相关事件
load- 资源加载完成unload- 文档卸载beforeunload- 文档即将卸载abort- 资源加载中断error- 资源加载失败
// load 事件不会冒泡
window.addEventListener('load', () => {
console.log('页面加载完成');
});
// 图片加载事件
const img = new Image();
img.addEventListener('load', () => {
console.log('图片加载完成,但事件不会冒泡');
});4. UI 事件
scroll- 元素滚动resize- 窗口大小改变
// scroll 事件不会冒泡
window.addEventListener('scroll', () => {
console.log('滚动事件,不会冒泡');
});
// 但可以在捕获阶段监听
document.addEventListener('scroll', () => {
console.log('在捕获阶段监听 scroll');
}, true);5. 媒体事件
play- 媒体开始播放pause- 媒体暂停ended- 媒体播放结束volumechange- 音量改变
const video = document.querySelector('video');
video.addEventListener('play', () => {
console.log('视频播放,事件不会冒泡');
});6. 其他不会冒泡的事件
DOMNodeInserted- 节点插入(已废弃)DOMNodeRemoved- 节点移除(已废弃)DOMAttrModified- 属性修改(已废弃)
处理不会冒泡的事件
方法1:使用捕获阶段
// 在捕获阶段监听 focus 事件
document.addEventListener('focus', (e) => {
console.log('捕获阶段监听到 focus:', e.target);
}, true); // true 表示捕获阶段方法2:使用会冒泡的替代事件
// 使用 focusin 替代 focus
document.addEventListener('focusin', (e) => {
console.log('focusin 会冒泡:', e.target);
});
// 使用 mouseover 替代 mouseenter
document.addEventListener('mouseover', (e) => {
console.log('mouseover 会冒泡:', e.target);
});方法3:事件委托的替代方案
// 对于不会冒泡的事件,需要直接为每个元素绑定
const inputs = document.querySelectorAll('input');
inputs.forEach(input => {
input.addEventListener('focus', handleFocus);
});
// 或者使用会冒泡的 focusin
document.addEventListener('focusin', (e) => {
if (e.target.matches('input')) {
handleFocus(e);
}
});检测事件是否会冒泡
function doesEventBubble(eventName) {
const element = document.createElement('div');
let bubbles = false;
element.addEventListener(eventName, (e) => {
bubbles = e.bubbles;
});
// 触发事件
const event = new Event(eventName);
element.dispatchEvent(event);
return bubbles;
}
console.log('focus 冒泡吗?', doesEventBubble('focus')); // false
console.log('click 冒泡吗?', doesEventBubble('click')); // true
console.log('mouseenter 冒泡吗?', doesEventBubble('mouseenter')); // false记忆技巧
Mnemonic: "FBM LUR"
Focus/Blur
Browser (load/unload/resize)
Mouseenter/Mouseleave
Load/Unload
UI events (scroll/resize)
Resource/media events
实际应用建议
表单验证:使用
focusin而不是focus进行事件委托鼠标悬停效果:使用
mouseover/mouseout而不是mouseenter/mouseleave进行事件委托性能优化:对于不会冒泡的事件,考虑是否需要为大量元素单独绑定