最近在复习,回过头看了一下事件流,将自己的理解和总结写一下.
补充了 event.currentTarget 和 event.target 的区别.
何为事件流
事件流是从页面中接受事件的顺序。
当你点击了页面的一个按钮的同时,你也点击了包括此按钮的容器元素,甚至也可以认为点击了整个页面。
为了解决这种情况,事件流的概念就诞生了。
事件流分为两种,由两个浏览器开发团队提出,分别是事件冒泡流
,事件捕获流
。
现在的浏览器基本上都实现了这两种事件流模型。
事件冒泡
顾名思义,就是从下往上传播。
在事件开始时,由最具体的元素接收事件,就是嵌套层次最深的那个节点,然后依次往上级元素传播,直到到达顶层对象(现在的浏览器实现都是冒泡到window
对象)
事件捕获
与冒泡模型相反,捕获模型是从上往下传播。
在事件开始,由顶层对象接收事件,然后沿着 DOM 树依次向下传播,直到到达事件的实际目标。(浏览器一般也是从 window 对象开始捕获事件的,)
1 | <html> |
当你点击 button,采用冒泡模型,事件会以如下的顺序传播
<button> -> <div> -> <body> -> <html> -> Document对象 -> window对象
采用捕获模型,事件会以如下的顺序传播
window对象 -> Document对象 -> <html> -> <body> -> <div> -> <button>
DOM 事件流
DOM2级事件
规定事件流包括三个阶段: 事件捕获阶段
、处于目标阶段
、事件冒泡阶段
捕获阶段
只从window对象
到达<div>
就停止了,下一个阶段是目标阶段
,事件在<button>
上发生,(事件处理会被看成冒泡阶段的一部分),然后冒泡阶段开始,事件由下往上传播。
事件处理程序
一个事件,可以是click
,也可以是mouseover
、DOMContentLoaded
、load
,详情参考MDN
响应一个事件的函数,叫做事件处理程序
,也可以叫事件监听器
,当事件发生时,对应的事件处理程序就会执行。
有三种方式可以注册事件监听器
1 | <html> |
事件委托
当页面中的事件处理程序过多的时候,会占用过多内存空间,解决办法就是事件委托
事件委托
利用了事件冒泡,只需要指定一个事件处理程序,就可以管理某一类型的事件。
实践
当你想给每一个 li 都添加一个事件处理程序时,你需要添加三个事件处理程序,当你添加的事件处理程序过多时,页面的性能会受到影响。
使用事件委托,只需在 DOM 树中尽量最高的层次上添加一个事件处理程序,可以有效地减少事件处理程序的数量。
1 | ;<ul id="List"> |
event 对象的 currentTarget 和 target 的区别
当多个有明显父子关系的元素都绑定了同一事件时,currentTarget 指向的是当前事件绑定的元素,event.target 指的是触发了该绑定事件的目标元素。
cunrrentTarget 发生在事件流的整个阶段(捕获,目标阶段,冒泡),event.target 只是指在目标阶段发生的元素。
通过下面的例子来理解
addEventListener 的第三个参数 useCapture 的默认值为 false,也就是说在冒泡阶段接收到该事件。
当点击 inner 元素时,事件从 inner 元素从下往上冒泡传播,所以 currentTarget 一开始指向的是 inner,随着事件冒泡传播,currentTarget 指向了其他绑定了 Click 事件的元素。只有在目标阶段的时候,event.target
才会等于 event.currentTarget
。
1 | currentTarget: inner |
如果把所有监听函数在注册时useCapture
参数的值置为 true,则说明在捕获阶段接收事件,则 inner 的父级元素会先于 inner 接收到 click 事件,所以结果打印顺序会颠倒,变为下面所示。
1 | currentTarget: first |