前言
在W3C事件模型中,一次事件的发生包含3个过程
1.捕获: 先由文档的根节点document(window )接收,然后逐级向下传播,直到具体的事件元素
2.目标: 事件源,触发事件
3.冒泡: 与捕获相反,逐级向上,直到文档根节点
(1)可以简单理解为事件发生的顺序不同,
(2)绑定事件方法addEventListener(“事件名”,事件function,true),第三个参数默认为false,即事件冒泡;若为true,即事件捕获。
1、事件冒泡
IE提出的事件流叫做事件冒泡,即开始时由具体的某一个子元素接收,然后逐级向上传播到父元素,
div>body>html>document>window
代码示例:
<div id="grandpa" style="width: 150px;height: 150px;background: red;">
<div id="dad" style="width: 100px;height: 100px;background: blue;">
<div id="son" style="width: 50px;height: 50px;background: yellow;">
</div>
</div>
</div>
效果如下
用addEventListener给三个div分别绑定点击事件
var grandpa = document.getElementById("grandpa");
var dad = document.getElementById("dad");
var son = document.getElementById("son");
grandpa.addEventListener("click", function (e) {
console.log("红")
})
dad.addEventListener("click", function (e) {
console.log("蓝")
})
son.addEventListener("click", function (e) {
console.log("黄")
})
点击黄色div,查看控制台如下:
先执行点击的元素的事件,然后由内向外逐步执行
2、事件捕获
网景公司提出的事件流叫事件捕获流。
事件捕获即从不确定的最外层节点慢慢向内执行,直到具体的事件源
下面添加addEventListener的第三个参数为true,模拟事件捕获
grandpa.addEventListener("click", function (e) {
console.log("红")
},true)
dad.addEventListener("click", function (e) {
console.log("蓝")
},true)
son.addEventListener("click", function (e) {
console.log("黄")
},true)
单击黄色div
可以看到由外向内执行
3、事件委托
前面说到的事件冒泡和事件捕获是事件在元素节点上发生的顺序不同,而事件委托只是利用了事件冒泡原理。
将原本作用在子元素的事件写在父元素,而子元素通过事件冒泡的原理,会触发绑定在父元素上的事件,这就是事件委托。
作用
(1)可以节省内存,不需要给所有子元素绑定事件
(2)新增的子元素依旧会拥有事件
下面说一个经典的例子:
描述:
ul包含了4个li标签,使每个li点击时弹出对应的内容
界面和代码如下:
<ul id="dad">
<li>我是一楼</li>
<li>我是二楼</li>
<li>我是三楼</li>
<li>我是四楼</li>
</ul>
<script>
var lilist = document.getElementsByTagName("li")
for(var i = 1; i < lilist.length; i++){
lilist[i].onclick = function(){
alert(this.innerHTML)
}
}
</script>
解析:
虽然说这种写法的代码很少,但实际上却对每一个li进行了遍历,给所有li都绑定了点击事件,如果存在多个li,也就是会有多个li的点击事件常驻在内存当中
改为事件委托的写法:
var ul = document.getElementsByTagName("ul")[0]
ul.onclick = function(e){
e = e || window.event;
//e.target代表事件源即被点击的li
alert(e.target.innerHTML)
}
效果:
解析:
这里我们给父元素一个点击事件,当子元素被点击的时候会通过事件冒泡去执行父元素的点击事件,而e.target会知道事件源是哪一个,即哪一个被点击
这样不管有多少个li元素,都只存在一个点击事件,可以减少遍历DOM的操作,提高性能。
?? 如果ul不只包含li标签,同时还存在其他子元素,比如span,p,但是我只想给li添加点击事件。按事件委托机制会出现什么效果呢
根据事件冒泡原理,当点击span,p标签时,依旧会触发父元素的事件,这就相当于span同样作用了该点击事件,所以这时候我们需要判断事件源e.target到底是哪一个
修改后的代码:
ul.onclick = function(e){
e = e || window.event;
//e.target代表事件源即被点击的li
if(e.target.nodeName.toLowerCase() == 'li'){
alert(e.target.innerHTML)
}
}
事件委托的另一个场景:
对于在页面载入之后新增的li标签,按最初将事件绑定在li标签的想法,新增的li时没有事件的。应为js代码页面刚加载去执行,此时只有原本的li标签存在事件。这时候就可以利用委托机制。
所以如果存在多个子元素需要绑定事件或新增元素同样需要事件,可以采用事件委托。