为了使DOM元素具有交互能力,例如,对用户点击这一动作做出响应,浏览器为DOM元素提供了事件机制。addEventListener
可以为元素绑定一个事件监听器:
// 假设 myButton 是一个按钮 |
DOM事件模型
想象以下场景:
<div id="div1"> |
当点击div3
时,其父元素也会触发被点击这一事件,也就是说,点击div3
,div1
、div2
、body
、html
、document
也被点击了,均会触发点击事件。
如果div1
、div2
、div3
这三个元素均添加了click事件监听器,那么哪个回调函数会先执行呢?
早期IE的观点是,应该先调用div3
的函数,然后调用div2
的函数,以此类推。也就是事件从里向外传播(propagation)。这个过程形象地被称为事件冒泡。
Netscape则认为,事件是由外向里传播,以上面的代码为例,事件会先传播到最外层元素div1
,然后再传播到div2
。如果div1
和div2
均对点击事件进行了监听,那么先执行的会是div1
的回调函数。这个过程被称为事件捕获。
为了兼容这两种事件流模型,2002年,W3C发布DOM2级事件模型,它规定了事件流包含三个阶段:事件捕获阶段、目标阶段、事件冒泡阶段。首先按事件捕获的顺序看有没有函数监听,然后按事件冒泡的顺序看有没有函数监听,如果有监听函数就调用,没有就跳过。开发者可以自己选择将监听函数放到捕获阶段还是冒泡阶段。
div.addEventListener('click', fn, bool) |
如果bool
不传或者为falsy
值,就让fn
走冒泡,即当浏览器在冒泡阶段发现div
上有fn
监听函数,就会调用fn,调用顺序按事件冒泡模型中的事件传播顺序来。如果bool
为true
,就在捕获阶段调用fn
。
事件委托
事件委托是事件流的一个典型应用。考虑以下场景:
<div id="parent"> |
现在想要给div
里的所有按钮都添加监听事件,如果分别给每个button
添加监听器,不仅代码量多,每个监听器都会占用内存,比较消耗资源。解决办法是,我们可以为父元素div
添加一个监听器,这样子元素button
被点击的时候事件也会触发。这就是事件委托。
事件委托的另一个应用是监听目前不存在,之后可能会动态添加的元素。可以监听这种动态元素的父元素,等点击的时候再判断是不是想要的元素。
Author: kpt
Permalink: http://kpt.ink/2021/08/15/DOM%E4%BA%8B%E4%BB%B6/
文章默认使用 CC BY-NC-SA 4.0 协议进行许可,使用时请注意遵守协议。