浏览器 - 事件流
一、浏览器 事件
1、事件流
事件流 描述的是从页面中接受事件的顺序。事件流 分为两种: 事件捕获 和 事件冒泡
2、addEventListener
EventTarget.addEventListener() 方法是将指定的监听器注册到事件目标上,当该对象触发指定的事件时,指定的回调函数就会被执行。
事件目标可以是一个文档上的元素 Element
, Document
和 Window
或者任何其他支持事件的对象。
addEventListener() 有三个参数,
第一个参数是事件类型,
第二个参数是一个函数,作为事件触发的函数。
第三个参数是一个布尔类型,是用来空值事件冒泡或者事件捕获的。 true: 事件捕获;false: 事件冒泡。第三个参数是可选的,默认是 false。
3、事件捕获
事件捕获 是Netscape团队提出的事件流。事件从最外层元素的事件开始,一级一级地传播到事件发生的具体元素。
Example:
<body id="body"> <div id="s1"> s1 <div id="s2">s2</div> </div> <script> s1.addEventListener("click",function(e) { console.log("s1 捕获事件"); }, true); s2.addEventListener("click",function(e) { console.log("s2 捕获事件"); }, true); body.addEventListener("click", function(e) { console.log("body级 捕获事件"); }, true) </script> </body>
点击 s2, 事件的执行顺序是: body -> S1 -> S2 。
4、事件冒泡
事件冒泡 是IE的事件流。 事件从事件开始的具体元素开始,一级一级朝着外部元素传播事件。
我们拿上面的事件捕获的例子距离,我们只需要把addEventListener()的第三个参数修改成 false。
Example:
<body id="body"> <div id="s1"> s1 <div id="s2">s2</div> </div> <script> s1.addEventListener("click",function(e) { console.log("s1 冒泡事件"); }, false); s2.addEventListener("click",function(e) { console.log("s2 冒泡事件"); }, false); body.addEventListener("click", function(e) { console.log("body级 冒泡事件"); }, false) </script> </body>
点击 s2,事件的执行顺序:S2 -> S1 -> body。
5、DOM事件流
"DOM2级事件" 事件总共分三个阶段:事件捕获阶段、目标阶段、事件冒泡阶段
Example:
<body> <div> <p id="parEle"> 我是父元素 <span id="sonEle"> 我是子元素 <span id="grandson">我是孙子元素</span> </span> </p> </div> </body> <script type="text/javascript"> var sonEle = document.getElementById('sonEle'); var parEle = document.getElementById('parEle'); parEle.addEventListener('click', function () { console.log('父级 冒泡'); }, false); parEle.addEventListener('click', function () { console.log('父级 捕获'); }, true); sonEle.addEventListener('click', function () { console.log('子级冒泡'); }, false); sonEle.addEventListener('click', function () { console.log('子级捕获'); }, true); grandson.addEventListener('click', function () { console.log('孙子级冒泡'); }, false); grandson.addEventListener('click', function () { console.log('孙子级捕获'); }, true); </script>
上面这个例子的执行顺序是: 父级捕获 -> 子集捕获 -> 孙子级冒泡 -> 孙子级捕获 -> 子级冒泡 -> 父级冒泡 。
在捕获阶段
又在冒泡阶段
调用事件处理程序时:事件按DOM事件流的顺序执行事件处理程序。
且当事件处于目标阶段时,事件调用顺序决定于绑定事件的书写顺序。
像上面的例子,如果将 孙子级两个事件互换位置,那么就会先执行捕获事件,再执行冒泡事件。
阻止冒泡事件的方法: e.stopPropagation()
阻止默认事件的方法: e.preventDefault()
6、target vs currentTarget 的区别
刚看到这个我查了一下。target 返回的是触发事件的元素,currentTarget 返回的是绑定事件的元素。
但是这样看起来并不是很清晰,举个例子:
<body id="father" onclick="myFunction(event)"> <p id="child" onclick="myFunction(event)">Click on child</p> </body> <script> function myFunction(event) { console.log(`currentTarget: ${event.currentTarget.id}, target: ${event.target.id}`); } </script>
当点击child元素的时候,因为浏览器的冒泡事件先执行child元素的点击事件,之后执行 parent 元素的点击事件。
事件的输出:
currentTarget: child, target: child currentTarget: father, target: child
可以看出,child 元素的点击事件中的 currentTarget 和 target 都是 child 元素本身。
冒泡到 father 元素之后,当前执行绑定事件元素则变为 father,因此 currentTarget 是 father;而触发事件的元素依旧是 child,所以 target 是 child。