javascript:浅谈事件冒泡、事件捕获以及事件委托

710 阅读4分钟

为什么我们要弄清楚捕捉和冒泡呢?那是因为,在过去糟糕的日子里,浏览器的兼容性比现在要小得多,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;
}

以上就是本文的内容,如果有不足或者有误的地方,欢迎大家批评指正。