前言
续上一章 addEventListener()和attachEvent()跨浏览器的兼容性处理 将的addEventListener和attachEvent比较的第四个不同点续讲…
事件冒泡
概念:
当一个元素接收到事件的时候 会把他接收到的事件传给自己的父级,一直到window 。(注意这里传递的仅仅是事件 并不传递所绑定的事件函数。所以如果父级没有绑定事件函数,就算传递了事件 也不会有什么表现 但事件确实传递了。)
这就话怎么理解 >>> 看代码
var div1 = document.getElementById("div1");
var div2 = document.getElementById("div2");
div2.onclick = function(){alert(1);};
div1.onclick = function(){}; //父亲
//html代码
<div id="div1">
<div id="div2"></div>
</div>
这里我们要注意,我们传递的仅仅是事件触发,也就是说当点击div2仅仅触发了父级的点击事件,并没有把自己的绑定的函数给父级,父级的执行情况,取决于自己所绑定的函数,因为在这里它绑定的函数是空,自然没什么表现。有些人在这里有误解,所以强调一下
讲一下addEventListener第三个参数:
常用的语法格式
target.addEventListener(type, listener[, useCapture]);
type
表示监听事件类型的字符串 ---- 例如mousedown, click, submit等事件名
listener
当所监听的事件类型触发时,会接收到一个事件通知(实现了 Event 接口的对象)对象。listener 必须是一个实现了 EventListener 接口的对象,或者是一个函数 ---- 也就是函数fn()
useCapture
true:
事件句柄在捕获阶段执行 ---- 也就是事件捕获,事件传播方向为外向里
false:
默认;事件句柄在冒泡阶段执行 ---- 也就是事件捕获,事件传播方向为里向外
这里对addEventListener只是简单叙述,详情可看
MDN;
代码展示(事件捕获的代码就不在展示啦,直接把false改为ture,输出结果就是 html body div):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body >
<!--html-->
<div style="width: 400px;height: 400px;background: red" >
事件冒泡(点击这里)
</div>
<script>
var div = document.querySelector("div");
div.addEventListener("click",function (e) {
alert("div")
},false)
var body = document.querySelector("body");
body.addEventListener("click",function (e) {
alert("body")
},false)
var html = document.querySelector("html");
html.addEventListener("click",function (e) {
alert("html")
},false)
// 输出结果: div body html
</script>
</body>
</html>
图解:
事件捕获
概念:
在事件捕获过程中, document 对象首先接收到 click 事件,然后事件沿 DOM 树依次向下,一直传播到事件的实际目标,是一种由外到内的传播
图解:
拓展:为什么要传播?
因为事件源本身(可能)并没有处理事件的能力,即处理事件的函数(方法)并未绑定在该事件源上。例如我们点击一个按钮时,就会产生一个click事件,但这个按钮本身可能不能处理这个事件,事件必须从这个按钮传播出去,从而到达能够处理这个事件的代码中(例如我们给按钮的onclick属性赋一个函数的名字,就是让这个函数去处理该按钮的click事件),或者按钮的父级绑定有事件函数,当该点击事件发生在按钮上,按钮本身并无处理事件函数,则传播到父级去处理。
来看一下一个经典的需求例子 引出stopPropagation和preventDefault
需求: 点击上面红色的面板让下面粉色的面板显示,点击其他地方再让粉丝面板隐藏;
代码:
<div id="div2" style="width: 100px;height: 100px;background: red;" >
点击2让下面的div显示,点击文档其他区域让他隐藏
</div>
<div id="div1" style="width: 100px;height: 100px;background: pink;display: none"></div>
var div2 = document.getElementById("div2");
var div1 = document.getElementById("div1");
div2.onclick = function(){ //红色面板事件
div1.style.display = "block";
};
document.onclick = function(){
div1.style.display = "none";
}
window中的on + type监听方法默认是【冒泡行为】
这个时候我们测试发现,怎么点击红色面板,粉丝面板都不会显示了,为什么呢?就是冒泡的原因,我们来分析下代码,当点击div2的时候,他会触发父亲级别的点击事件,然后一层一层的往上传,所以document的点击事件自然也被触发了,然后执行了自己身上的绑定事件,让粉色面板消失。所以当你点击div2的时候首先让粉色面板显示,只是事件执行太快了,很快又执行了document的点击事件,让面板隐藏。 有兴趣的可以再两个事件之间加一个弹出,这样就可以看到这个快速的过程啦
那么此时就可以通过组织事件冒泡解决上面的需求啦
取消事件冒泡有两种方式:
标准的W3C 方式:e.stopPropagation();这里的stopPropagation是标准的事件对象的一个方法,调用即可
非标准的IE方式:e.cancelBubble=true; 这里的cancelBubble是 IE事件对象的属性,设为true就是取阻止事件冒泡
同样(事件的默认行为):
标准的W3C 方式:e.preventDefault();这里的preventDefault是标准的事件对象的一个方法,调用即可
非标准的IE方式:e.returnValue=false; 这里的returnValue是 IE事件对象的属性,设为false就是取消事件的默认行为
通常我们会封装这样一个兼容性函数:
var preStop = {
stopPropagation: function(e){
var e = e || window.event;
if(e.stopPropagation){
e.stopPropagation
}else{
e.cancelBubble = true;
}
},
preventDefault: function(e){
var e = e || window.event;
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}
}
}
那么上面的需求代码改成这样就可以啦 — 完成
var div2 = document.getElementById("div2");
var div1 = document.getElementById("div1");
var preStop = {
stopPropagation: function(e){
var e = e || window.event;
if(e.stopPropagation){
e.stopPropagation();
}else{
e.cancelBubble = true;
}
},
preventDefault: function(e){
var e = e || window.event;
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}
}
}
div2.onclick = function(event){ //红色面板事件
div1.style.display = "block";
preStop.stopPropagation(event);
};
document.onclick = function(){
div1.style.display = "none";
}
完结
其实冒泡还有一大优点,就是事件委托,而且经常用到,还能提高很大的性能,想要了解可以看这个事件委托