为什么我们要弄清楚捕捉和冒泡呢?那是因为,在过去糟糕的日子里,浏览器的兼容性比现在要小得多,Netscape(网景)只使用事件捕获,而Internet Explorer只使用事件冒泡。当W3C决定尝试规范这些行为并达成共识时,他们最终得到了包括这两种情况(捕捉和冒泡)的系统,最终被应用在现在浏览器里。
粗略的理解就是当一个事件发生在具有父元素的元素上时,现代浏览器运行两个不同的阶段 - 捕获阶段和冒泡阶段。
冒泡还允许我们利用事件委托——这个概念依赖于这样一个事实,如果你想要在大量子元素中单击任何一个都可以运行一段代码,您可以将事件监听器设置在其父节点上,并让子节点上发生的事件冒泡到父节点上,而不是每个子节点单独设置事件监听器。
dom的事件标准定义了俩种事件流:事件冒泡和事件捕获,dom的事件处理流程分为三个阶段,事件捕获,目标事件,事件冒泡。
一、事件冒泡
相信很多前端小伙伴都遇见过这样的问题,就是给子盒子和父盒子添加点击事件,点击子盒子绑定的事件,同时也会触发父盒子绑定的事件。其实这种现象就叫做JavaScript的事件冒泡。
之所以会出现这种情况,是因为js的事件传播过程是目标元素触发事件后,该事件会逐级传播给祖先元素,直到document为止,有的浏览器可能到window。
<div id="bigBox">外盒子
<div id="smallBox">内盒子</div>
</div>
var bigBox = document.getElementById("bigBox");
var smallBox = document.getElementById("smallBox");
bigBox.onclick=function(){
console.log('a');
};
smallBox.onclick=function(){
console.log('b');
};
该例子中,如果点击内盒子,则会依次打印出”b”和”a”,如果点击外盒子,则只打印”a”。 需要注意的是,并不是所有的事件都有冒泡现象,比如blur、focus和load等事件就没有。
阻止事件冒泡:
//阻止事件冒泡
let btna = document.getElementById('btn');
btna.onclick=function(e){
window.event? window.event.cancelBubble = true : e.stopPropagation();
};
//或者
return false;
注意:该方法不仅可以阻止冒泡,还可以阻止捕获和处于目标阶段的事件流。
** 二、事件捕获**
事件捕获和事件冒泡相反,它从外部的祖先元素开始,传播到事件触发的元素。js事件捕获一般通过DOM2事件模型addEventListener来实现的:
target.addEventListener(type, listener, useCapture)。第三个参数默认设置为false,表示在冒泡阶段触发事件,设置为true时表示在捕获阶段触发(解绑事件的方法为removeEventListener('click', bodyClick, false))。
<div id="box">
<div id="middle">
<div id="inner"></div>
</div>
</div>
//事件捕获
window.onload=function(){
let box=document.getElementById("box");
let middle=document.getElementById("middle");
let inner=document.getElementById("inner");
box.addEventListener("click",function(){console.log("box")},true);
middle.addEventListener("click",function(){console.log("middle")},true);
inner.addEventListener("click",function(){console.log("inner")},true);
}
当点击inner绑定事件时,控制台会直接输出,box,middle,inner
三、事件委托
在JavaScript中,绑定到元素上的事件数量将直接关系到页面的整体运行性能,因为需要不断的操作dom,那么引起浏览器重绘和回流的可能也就越多,这就需要我们尽量少的操作dom。本来需要绑定多个事件的情况,现在只需要绑定一个事件就可以实现原有的效果,这样做一方面可以减少内存被占用的空间,另一方面对应用的整体性能提升也有积极的作用 。如果要用事件委托,就会将所有的操作放到js程序里面,只对它的父级(如果只有一个父级)这一个对象进行操作,与dom的操作就只需要交互一次,这样就能大大的减少与dom的交互次数,提高性能。
事件委托,也叫事件代理,主要是利用事件冒泡原理,从目标节点开始,逐渐向上传播事件,最终将事件委托给它的父级节点。
根据上面的描述,我们知道事件委托可以使代码逻辑更加简洁高效,并且减少操作dom的次数,把类似元素的事件绑定委托给其父元素进行统一的监听处理,方便动态的添加和修改元素。
适合事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress等; 需要注意的是,事件委托都是利用了事件冒泡的原理,所以没有冒泡属性的事件是指定不能进行事件委托处理的,例如focus,blur,load等事件。
相关面试题:在body中添加十个div标签,点击每个标签弹出相应的内容
for (let i = 1; i <= 10; i++) {
let div = document.createElement('div')
div.innerHTML = i + '<br>'
document.body.appendChild(a)
div.addEventListener('click', function(e) {
e.preventDefault()
alert(i)
})
}
附:阻止浏览器默认行为:
//阻止浏览器的默认行为
function stopDefault( e ) {
if ( e && e.preventDefault ){//一般情况下
e.preventDefault();
}else{//IE中
window.event.returnValue = false;
}
return false;
}
以上就是本文的内容,如果有不足或者有误的地方,欢迎大家批评指正。