DOM 事件模型
DOM 的事件操作(监听和触发),都定义在 EventTarget 接口。所有节点对象都部署了这个接口,其他一些需要事件通信的浏览器内置对象(比如:XMLHttpRequest、AudioNode、AudioContext)也部署了这个接口。
该接口主要提供三个实例方法。
addEventListener // 绑定事件的监听函数
removeEventListener // 移除事件的监听函数
dispatchEvent // 触发事件
复制代码
事件模型
一个事件发生后,会在子元素及父元素之间进行传播(propagation),这种传播分为三个阶段。
(这种三阶段的传播模型,使得同一个事件会在多个节点上触发。)
- 由外向内找监听函数就是事件捕获
- 在目标节点触发事件
- 由内而外找监听函数就是事件冒泡
通俗一点来说就是一个事件被触发时,浏览器会自动从用户操作标签外的最上级标签逐渐向里检查是否有相同事件,如果有则触发,如果没有则继续向下检查知道用户操作的标签,这过程称为捕获,此时浏览器会继续由用户操作标签继续向是上级标签检查,如果有相同事件则触发,如果没有则继续向上检查直到最上级元素为止,此过程称为冒泡。(有监听函数就执行,并提供事件信息,没有就跳过)
事件传播的最上层对象是 window ,上例的事件传播顺序,在捕获阶段依次为 window、document、html、body、父节点、目标节点(即大到小),在冒泡阶段依次为目标节点、父节点、body、html、document、window (即小到大)。
DOM 事件传播的三个阶段(W3C 标准):捕获阶段,目标阶段,冒泡阶段
点击事件
有以下代码
<div class="grandfather">
<div class="father">
<div class="son">123</div>
</div>
</div>
复制代码
给以上三个div分别添加事件监听 fnYe/fnBa/fnEr
在点击了“123”时,爷爷、爸爸、儿子均可算被点击了;在点击了“123”时,调用 fnYe/fnBa/fnEr 中的哪个函数均可行。
IE5 认为先调用 fnEr,网景认为先调用 fnYe,最后遇到了 W3C
从外部向内部寻找监听函数,此过程叫做事件捕获(网景提出)
baba.addEventListener('click', fn) // 捕获
复制代码
从内部向外部寻找监听函数,此过程叫做事件冒泡(IE5提出)
baba.attachEvent('onclick', fn) // 冒泡
复制代码
W3C 作为和事老,提出一个标准
baba.addEventListener('click', fn, bool)
复制代码
以上代码中:
bool 不传 或为 falsy 时,就让 fn 走冒泡,即当浏览器在冒泡阶段发现 baba 有 fn 监听函数,就会调用 fn ,并提供时间信息。
bool 为 true 时,就让 fn 走捕获,即当浏览器在捕获阶段发现baba有fn监听函数,就会调用 fn ,并且提供事件信息。
总结
捕获冒泡
捕获:调用爸爸的监听函数
冒泡:先调用儿子的监听函数
W3C事件模型
先捕获(爸爸=>儿子)再冒泡(儿子=>爸爸)
调用函数时并不会调用两次,开发者可以自己决定把要调用的函数放在捕获阶段还是冒泡阶段
注意以下两点:
- e 对象会被传给所有的监听函数
- 事件结束后, e 对象就不存在了
target 和 currentTarget 的区别
区别:
e.target // 用户操作的元素
e.currentTarget // 程序员监听的元素
this 是 e.currentTarget,不推荐使用
示例:
div>span{文字} // 用户点击文字
以上代码 e.target 就是 span
e.currentTarget 就是 div
一个特例
假如:
只有一个 div 被监听(不考虑父子同时被监听)
fn 分别在捕获阶段和冒泡阶段监听 click 事件
用户点击的元素就是开发者监听的元素时
有以下代码:
div.addEventListenter('click',f1)
div.addEventListenter('click',f2,true)
复制代码
此时是f1先执行还是f2先执行?如果把这两行代码位置调换后谁先执行?
答:谁先被监听,谁先执行。
e.stopPropagation(): // 取消冒泡
e.stopPropagation()
复制代码
打断冒泡,浏览器不再向上走
一般用于封装某些独立组件
注意:捕获不可以取消但是冒泡可以