Part 1:事件机制
事件机制
DOM 的事件操作(监听和触发),都定义在EventTarget接口。所有节点对象都部署了这个接口,其他一些需要事件通信的浏览器内置对象(比如,XMLHttpRequest、AudioNode、AudioContext)也部署了这个接口。
该接口主要提供三个实例方法。
- addEventListener:绑定事件的监听函数
- removeEventListener:移除事件的监听函数
- dispatchEvent:触发事件
捕获和冒泡
一个事件发生后,会在子元素及父元素之间进行传播(propagation),这种传播分为三个阶段。
(这种三阶段的传播模型,使得同一个事件会在多个节点上触发。)
- 由外向内找监听函数就是事件捕获
- 在目标节点触发事件
- 由内而外找监听函数就是事件冒泡 通俗一点来说就是一个事件被触发时,浏览器会自动从用户操作标签外的最上级标签逐渐向里检查是否有相同事件,如果有则触发,如果没有则继续向下检查知道用户操作的标签,这过程称为捕获,此时浏览器会继续由用户操作标签继续向是上级标签检查,如果有相同事件则触发,如果没有则继续向上检查直到最上级元素为止,此过程称为冒泡。(有监听函数就执行,并提供事件信息,没有就跳过)
事件传播的最上层对象是window,上例的事件传播顺序,在捕获阶段依次为window、document、html、body、父节点、目标节点,在冒泡阶段依次为目标节点、父节点、body、html、document、window。
DOM事件传播的三个阶段:捕获阶段,目标阶段,冒泡阶段
addEventListener
事件绑定API
- IE5*:baba.attachEvent('onclick',fn) //冒泡
- 网景:baba.addEventListener('click',fn) //捕获
- W3C:baba.addEventListener('click',fn,bool)
如果bool不传或为falsy
就让fn走冒泡,即当前浏览器在冒泡阶段发现baba有fn监听函数,就会调用fn,并提供事件信息
如果bool为true
就让fn走捕获,即当浏览器在捕获阶段发现baba有fn监听函数,就会调用fn,并提供事件信息
总结
两个疑问:
儿子被点击,算不算点击老子?
- 算
那么先调用老子得函数还是先调用儿子的函数?
- 都可以
捕获冒泡
捕获说先调用爸爸的监听函数
冒泡说先调用儿子的监听函数
W3C事件模型
先捕获(先爸爸=>儿子)再冒泡(再儿子=>爸爸)
注意e对象被传给所有的监听函数
事件结束后,e对象就不存在了
target v.s. 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()可打断冒泡,浏览器不再向上走
一般用于封装某些独立组件
注意:捕获不可以取消但是冒泡可以
不可以取消冒泡 有些事件不可以取消冒泡
可以查阅MDN英文版冒泡
如何禁用滚动
取消特定元素的wheel和touchstart的默认动作
Part 2:事件委托
什么是事件委托
事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。通俗地来讲,就是把一个元素响应事件(click、......)的函数委托到另一个元素; 一般来说,会把一个或者一组元素的事件委托到它的父层或者更外层元素上,真正绑定事件的是外层元素,当事件响应到需要绑定的元素上时,会通过事件冒泡机制从而触发它的外层元素的绑定事件上,然后在外层元素上去执行函数。