事件流
有时候我们在网页中多个地方都绑定了点击事件,但是当你点击其中一个,你会发现其他的绑定事件也被触发了,是不是觉得有点灵异,实际上是浏览器的事件流在作怪。
假设我们在页面上有一个按钮,那么当我们单击它时,你们是不是认为单击事件仅仅只会发生在这个按钮上?这其实只是我们理所当然的认为而已。想象一下,现在有一张画了很多个同心圆的纸,你把手指放在圆心上面,那么你的手就指向了最小的圆吗,不,事实并不是这样,你其实指向了纸上的所有圆。
在浏览器页面上,众多浏览器开发团队也认为你在单击按钮的同时,也单击了包含按钮的容器元素,甚至也单击了整个页面
。
很奇葩的是IE和Netscape开发团队提出了两种相反的事件流的概念。IE的事件流是事件冒泡流,而Netscape的事件流是事件捕获流。
事件冒泡
IE的事件流叫做事件冒泡,即开始时由最具体的元素(文档中嵌套层次最深的那个节点),通常也是我们点击的节点接收,然后逐级向上传播。
别的啥都不说,看代码说话
EXP1
|
|
现在,当你点击了div元素时,传播路径是这样的:
- div
- body
- html
- document
看到没有,原来你不止触发了一个点击事件,只要你在body或者html上绑定一个事件,你会发现,他们都会在div事件触发之后被触发。现代浏览器都支持事件冒泡,只是有少许差别。IE9、FF、chrome、Safari会将事件一直冒泡到window对象,不过我们不用太在意。
下面在上面的代码基础上我们再做一些修改
EXP2
|
|
但我们点击最小的区域块,即黄色的child,效果图如下:
我们只点击了child子元素,但是却也触发了parent和ancestor的点击事件!
取消冒泡
有些情况下,我们并不希望子元素的事件影响父元素,这时我们就要阻止冒泡事件了,具体兼容代码如下:
|
|
下面我们再看看事件捕获又是怎么一回事。
事件捕获
事件捕获是由Netscape提出的另一种事件流,它与冒泡恰好相反,是由顶层至上而下传播的。
如果在捕获状态下触发第一个例子的话,传播路径则相反过来了:
- document
- html
- body
- div
document事件首先接受到click事件,然后沿DOM树依次向下一直传递到到我们所点击的div元素,至此,捕获事件才算完成。
我们对例二进行一下修改,来看看捕获的实际过程。
EXP3
|
|
我们依旧点击黄色的child,效果如下:
因为老版本浏览器不支持,所以并不建议使用它,除非特殊需要。
DOM事件流
任何发生在w3c事件模型中的事件,首是进入捕获阶段,直到达到目标元素,再进入冒泡阶段
默认情况下,执行的是时间冒泡。
那么我们要如何让事件在捕获或者冒泡时执行呢?
事件绑定方法
这里我们只涉及addEventListener、attachEvent这两个绑定方法。
addEventListener(event,func,useCapture)
参数:
event:事件名称,如click,不带on func:用于监听的函数 useCapture:是否进行捕获,默认为false即冒泡
addEventListener在IE11、Chrome、FF、Safiri等浏览器都支持
attachevent(event,func)
参数:
event:事件名称,如onclick,带on func:用于监听的函数
attachEvent仅在IE10及以下才支持.
事件委托
说完了事件的冒泡、捕获和绑定方法,下面综合这些知识点来讲讲事件委托。
有时候我们需要给很多子元素添加事件,这时候我们可以选择另一种方式,把事件添加到父节点即将事件委托给父节点来触发处理函数。这主要得益于Dom事件流的事件冒泡机制。
假设我们有一个ul列表,需要给下面的子节点绑定函数:
现在我们要求点击每个li元素的时候在console打印出他的content。最普遍的写法,就是给每个li元素通过onClick的方式绑定监听函数。
这样做不仅费力,当li元素的个数增加或者减少时,我们就需要手动为新添加的元素绑定函数。更好的方式是使用事件委托机制,把事件绑定到父元素或者更上层的节点,通过检查时间的目标对象(event.target)来获取事件源。
|
|
我们通过e.target拿到被点击的L节点,除此之外e还有一个currentTarget属性,里面保存的是触发事件的当前对象,这里为ul元素。
掌握好这些事件流的知识点对我们今后的开发会很有帮助,大家可以上网多搜搜捕获冒泡及委托的例子学习下!