事件✅
事件冒泡是什么?
答案
参考 w3c ui event 冒泡机制是为了解决在如何在 DOM 树上广播事件。整个广播流程参考下图:
整个事件从创建到完成的流程如下
- 事件产生,用户点击等操作,此时会创建一个
event target
对象包含出发事件时的相关信息target
指向触发事件的元素currentTarget
只读属性指向事件分发过程中的当前对象- 在回调函数中
this
指向 currentTarget.
- 根据当前触发元素确定传播路径
- 事件分发,此时时间会在 DOM 树上进行广播,广播分为三个阶段
- 捕获阶段 从传播路径的根元素向目标元素传播,从 window 开始
- 目标阶段 当传播到达事件目标时
- 冒泡阶段 当从事件目标对象向父元素广播时从 window 结束。
事件广播过程会修改
event target
相关状态,此外可以利用stopPropagation
对事件传播进行截断。
- 事件处理阶段,在事件分发过程中若传播路径有元素绑定了回调事件,
event target
对象作为传入参数触发执行。
但目标元素触发事件时,事件的分发分为三个阶段:
- 捕获阶段(capture phase) 从根元素逐级向目标元素传递
- 目标阶段(target phase) 传递到目标元素的阶段
- 冒泡阶段(bubbling phase) 从目标元素重新传递到根元素的阶段
事件委托的作用和意义?
答案
事件委托是一种常用的事件处理模式,主要通过将事件监听器添加到父元素上,而不是每个子元素上,从而提高性能和简化代码。其作用和意义包括:
- 提高性能:减少事件监听器的数量,降低内存消耗,尤其在子元素较多时更为明显。
- 简化代码:通过事件冒泡,父元素可以处理所有子元素的事件,避免为每个子元素单独绑定事件。
- 动态添加元素:对于动态生成的子元素,父元素的事件监听器可以自动响应,无需重新绑定事件。
说明 load,ready,DOMContentLoaded 的区别
答案
属性 | DOMContentLoaded | load | ready |
---|---|---|---|
触发时机 | DOM结构解析完成 | 页面所有资源加载完毕 | DOM结构解析完成 |
典型用途 | 尽早操作DOM、初始化交互 | 依赖全部资源的操作 | 早期DOM操作、兼容性处理 |
是否等待外部资源 | 否 | 是 | 否 |
适用范围 | 原生JS | 原生JS | jQuery专用 |
// 原生 DOMContentLoaded
document.addEventListener('DOMContentLoaded', () => {
// DOM 可操作
})
// 原生 load
window.addEventListener('load', () => {
// 所有资源加载完毕
})
// jQuery ready
$(function () {
// DOM 可操作
})
推荐优先使用 DOMContentLoaded
或 jQuery 的 ready
,可提升页面响应速度和用户体验。
load
事件需等待所有资源加载,页面大图或慢资源会延迟触发,影响初始化时机。
延伸阅读
- MDN: DOMContentLoaded — 官方事件说明
- jQuery ready 文档 — jQuery 兼容性处理
- 资源加载和页面事件详解 — 详细流程与原理
stopImmediatePropagation 和 stopPropagation 区别
答案
stopPropagation
和 stopImmediatePropagation
都用于阻止事件冒泡,但作用范围不同:
|方法|作用范围|典型场景| |event.stopPropagation()|阻止事件继续向父元素冒泡,但同一元素上后续监听器仍会执行|只需阻止冒泡,不影响本元素其他事件处理| |event.stopImmediatePropagation()|阻止事件冒泡,并阻止当前元素上后续所有同类型事件监听器执行|彻底阻断冒泡和本元素后续处理|
const btn = document.getElementById('btn')
btn.addEventListener('click', () => { console.log('handler1') })
btn.addEventListener('click', (e) => {
e.stopImmediatePropagation()
console.log('handler2')
})
btn.addEventListener('click', () => { console.log('handler3') })
/*
点击 btn 时,只输出 handler2,handler1/handler3 都不会执行
*/
如果用 stopPropagation()
,则 handler1、handler2 都会执行,handler3 不会因冒泡被阻止。
stopPropagation
只影响冒泡,不影响同一元素的其他监听器。stopImmediatePropagation
更彻底,常用于防止重复触发或冲突处理。
如需彻底阻止事件在当前元素的所有后续处理,优先用 stopImmediatePropagation
。
延伸阅读
addEventListener 和 attribute onclick
答案
为什么 {capture:true}
不是先触发
参见此回答 Event listeners registered for capturing phase not triggered before bubbling - why?
addEventListener 绑定事件?参数不同的执行顺序
mouseEnter、mouseLeave、mouseOver、mouseOut 有什么区别?
答案
下表总结了四个鼠标事件的主要区别:
事件名 | 触发时机 | 是否冒泡 | 子元素影响 | 典型用途 |
---|---|---|---|---|
mouseenter | 鼠标进入元素本身 | 否 | 否 | 只需关注本元素的进入 |
mouseleave | 鼠标离开元素本身 | 否 | 否 | 只需关注本元素的离开 |
mouseover | 鼠标进入元素或其子元素 | 是 | 是 | 需监控元素及子元素的进入 |
mouseout | 鼠标离开元素或其子元素 | 是 | 是 | 需监控元素及子元素的离开 |
element.addEventListener('mouseenter', () => console.log('mouseenter'))
element.addEventListener('mouseleave', () => console.log('mouseleave'))
element.addEventListener('mouseover', () => console.log('mouseover'))
element.addEventListener('mouseout', () => console.log('mouseout'))
有嵌套子元素时,推荐用 mouseenter
/mouseleave
,可避免鼠标在父子元素间频繁触发事件。
mouseover
/mouseout
适合需要对子元素进入/离开也做处理的场景,如复杂菜单。
延伸阅读