什么是事件冒泡和事件委托

Author Avatar
zzz1220 3月 03, 2018
  • 在其它设备中阅读本文章
👉👉本文共1.2k字📘 读完共需4分钟⌚

自从有了javaScript这个浏览器端的脚本语言,用户们终于可以告别单调的展示网页了,javaScript给网页带来了新的生命力,网页前端从此变得有趣起来,最主要的,网页上的元素有了事件这个元素,但是关于事件,就不得不提事件冒泡和事件委托这两个关键的知识点。

什么是事件?

JavaScript与html之间的交互全部都是由事件实现的。事件就是你操作鼠标或者键盘,对网页进行交互,比如你点击一个按钮,就触发了一个onClick事件,你改变input节点内的value,就出发了onChange事件。

事件流和事件冒泡

你可以把网页想象成几个同心圆,当你点击最中心的圆的时候,也相当与点击外层的几个圆了,所以说,当你点击页面上的一个按钮,就相当于点击了整张页面,会触发这个按钮的所有父节点的click事件,这个就是事件流。但是当时ie和其他的浏览器偏偏要不一样,ie支持的是冒泡流,而网景提出的是捕获流,后来,在w3c组织的统一下,js支持冒泡流和捕获流,但是呢,某些低版本的浏览器(ie6,ie7,ie8)却只支持冒泡流。直接用onClick就是在冒泡阶段添加事件。

<div in="outer">
    <div id="inner"></div>
</div>

对于事件捕获来说,是从外到内的,点击#inner的div,会从window开始捕获,然后是document,html,…,一直到#outer,再到#inner。
对于事件冒泡来说,是从内到外的,点击#inner的div,会从#inner开始,到#outer,…,最后是window。

dom事件流分为三个阶段
1.事件捕获。
2.处于目标阶段。
3.事件冒泡。

可以分别给dom元素添加事件流事件

document.addEventListener('click',function(){
            console.log('document处于事件捕获阶段');
        }, true);// true是捕获阶段,false是冒泡阶段。

问题来了,如果不想事件冒泡怎么办?我们点击子节点,不想触发父节点的事件。可以这样做:

stopPropagation() 方法
终止事件在传播过程的捕获、目标处理或起泡阶段进一步传播。调用该方法后,该节点上处理该事件的处理程序将被调用,事件不再被分派到其他节点。

事件委托

先说下event对象,这个对象会作为事件的默认参数。
api:

bubbles	返回布尔值,指示事件是否是起泡事件类型。
cancelable	返回布尔值,指示事件是否可拥可取消的默认动作。
currentTarget	返回其事件监听器触发该事件的元素。
eventPhase	返回事件传播的当前阶段。
target	返回触发此事件的元素(事件的目标节点)。
timeStamp	返回事件生成的日期和时间。
type	返回当前 Event 对象表示的事件的名称。

为啥要进行事件委托,我们都知道dom操作是很花费时间的。与dom节点交互,访问dom节点,非常影响性能。比如要给ul下的li都添加上click事件,最简单的方法,写个for循环,挨个click就行了,但是这样要多次操纵dom节点。利用事件委托的方式,把click事件绑定到ul上,因为事件冒泡的存在,会触发ulclick事件,而且就一次dom操作,那怎么判断点击的是哪一个li呢?这时候上面的event对象就起作用了,event对象提供了一个属性叫target,可以返回事件的目标节点,我们成为事件源,也就是说,target就可以表示为当前的事件操作的dom,但是不是真正操作dom,当然,这个是有兼容性的,标准浏览器用ev.target,IE浏览器用event.srcElement。
这时候我们就拿到了具体点击的dom节点,然后用nodeName获取节点名,在函数内部判断下,就实现把dom操作放在js代码内部来实现了。
而且这样做还有一点好处就是,新添加的li标签不会点击无效,如果直接把事件绑定在li上,每添加li都要重新绑定事件。

<ul id="ul">
<li></li>
</ul>


window.onload = function(){
		      var oUl = document.getElementById("ul");
		      oUl.onclick = function(ev){
		    	    var ev = ev || window.event;
		   		    var target = ev.target || ev.srcElement;
		            if(target.nodeName.toLowerCase() == 'li'){
		                	alert(123);
		         alert(target.innerHTML);
				    }
		      }
		}