本篇主要介绍HTML DOM中的事件流和事件委托。
其他事件文章
3. HTML 事件(三) 事件流与事件委托
目录
1. 事件流
1.1 何为事件流
1.2 事件流的三个阶段
1.3 addEventListener()注册事件流的阶段
1.4 阻止事件流的传播
2. 事件委托
2.1 何为事件委托
2.2 ul、li场景示例
2.3 JQuery的事件委托
2.3.1 delegate()
2.3.2 on()
1. 事件流 (Event Flow)
1.1 何为事件流
简单来说HTML的元素会出现嵌套的关系,比如:一个Div嵌套了一个Button按钮,当2个元素都注册了click点击事件。点击里面的Button按钮时,Div的click事件也会触发。
那么问题来了,既然都会触发,得有个触发的顺序吧?是按照Div → Button这样的顺序触发,还是按照Button → Div的顺序触发?
在之前,两大浏览器厂商网景和微软都有各自的触发顺序:
网景的浏览器采用捕获方式:按照Div → Button这样的顺序触发.
而微软的浏览器采用冒泡方式:按照Button → Div的顺序触发。
在2级DOM事件规范制定时干脆合二为一:事件流同时包含了这2个阶段。
1.2 事件流的三个阶段
1.2.1 三个阶段
2级DOM事件规范制定了事件流的三个阶段:捕获阶段、目标阶段和冒泡阶段:
捕获阶段(Capture Phase):事件从最外层的window对象到目标节点的父节点依次触发的阶段。(从外到内)
目标阶段(Target Phase):事件在目标节点上触发时的阶段。
冒泡阶段(Bubbing Phase):事件从目标节点的父节点到最外层的window对象依次触发的阶段。(从内到外)
1.2.2 示例图
1.3 addEventListener()注册事件流的阶段
元素对象通过addEventListener()注册事件时,此方法的的第三个参数可设置本次注册是捕获阶段还是冒泡阶段。
1.3.1 addEventListener()方法说明
语法:EventTarget.addEventListener(eventName, eventHandler [, useCapture] )
参数:
①eventName {string} :所要注册的事件名称,不区分大小写。此名称不需要像注册事件属性那样在前缀加上"on"。如注册鼠标点击事件,写为click。
②eventHandler {function | function Object} :函数或者函数对象。事件触发时所需要执行的函数;当使用函数对象多次注册同一事件时,只当注册一遍。
③useCapture {boolean} 可选 :是否处于捕获阶段,默认为false。
|-true:当前注册的事件为捕获阶段。
|-false:当前注册的事件不为捕获阶段,为冒泡阶段。
1.3.2 示例
示例使用到Event事件对象的部分属性:
readonly Object currentTarget :只读,获取正在处理此事件的对象。
readonly int eventPhase :只读,表示事件的处理阶段:0表示没有正在处理,1表示捕获阶段,2表示目标阶段,3表示冒泡阶段。
readonly Object target :只读,获取触发此事件的对象。
function clickHandle(e){
console.log("事件阶段:"+e.eventPhase+';target:'+e.target+';currentTarget:'+e.currentTarget)
} window.addEventListener('click',clickHandle,false);
window.addEventListener('click',clickHandle,true);
document.addEventListener('click',clickHandle,false);
document.addEventListener('click',clickHandle,true);
document.documentElement.addEventListener('click',clickHandle,false);
document.documentElement.addEventListener('click',clickHandle,true);
document.body.addEventListener('click',clickHandle,false);
document.body.addEventListener('click',clickHandle,true);
document.getElementById('div').addEventListener('click',clickHandle,false);
document.getElementById('div').addEventListener('click',clickHandle,true);
document.getElementById('btn').addEventListener('click',clickHandle,false);
document.getElementById('btn').addEventListener('click',clickHandle,true);
1.4 阻止事件流的传播
Event 事件对象的stopPropagation()、stopImmediatePropagation()方法可阻止事件流的后续传播。
注:stopImmediatePropagation()方法除了阻止事件流传播还会阻止当前事件在此元素的后续事件处理程序。
事件流的三个阶段调用这2个方法,会有不同的阻止传播方式:
1.4.1 在捕获阶段调用
说明:在捕获阶段调用stopPropagation()方法时,此元素后续的事件流都会阻止,包括捕获阶段、目标阶段、冒泡阶段。
示例:在1.3.2示例代码中的body元素捕获阶段调用此方法
document.body.addEventListener('click',function(e){
console.log("事件阶段:"+e.eventPhase+';target:'+e.target+';currentTarget:'+e.currentTarget)
e.stopPropagation();
},true);
结果:事件流在body的捕获阶段就截至了,后续的阶段都没有执行
1.4.2 在目标阶段调用
说明:在目标段调用stopPropagation()方法时,捕获阶段和目标阶段会执行完毕,冒泡阶段不会被执行。
示例:在1.3.2示例代码中的button按钮元素目标阶段调用此方法
document.getElementById('btn').addEventListener('click',function(e){
console.log("捕获阶段注册:事件阶段:"+e.eventPhase+';target:'+e.target+';currentTarget:'+e.currentTarget)
e.stopPropagation();
},false);
document.getElementById('btn').addEventListener('click',function(e){
console.log("冒泡阶段注册:事件阶段:"+e.eventPhase+';target:'+e.target+';currentTarget:'+e.currentTarget)
e.stopPropagation();
},true);
结果:捕获阶段和目标阶段执行完毕,冒泡阶段未被执行。
1.4.3 在冒泡阶段调用
说明:在冒泡段调用stopPropagation()方法时,捕获阶段和目标阶段会执行完毕,元素后续的冒泡阶段不会被执行。
示例:在1.3.2示例代码中的body冒泡阶段调用此方法
document.body.addEventListener('click',function(e){
console.log("事件阶段:"+e.eventPhase+';target:'+e.target+';currentTarget:'+e.currentTarget)
e.stopPropagation();
},false);
结果:捕获阶段和目标阶段执行完毕,body后续的冒泡阶段未被执行
2. 事件委托(Event Delegate)
2.1 何为事件委托
HTML元素含有嵌套关系,并且事件流含有冒泡阶段。子元素的触发事件会冒泡到父元素的相同事件上。
一般情况只需给子元素注册特定的事件处理程序即可,但当子元素过多或频繁的进行增减操作怎么办?
比如一个ul包含了几十个li元素,对每个li元素进行单独的事件注册会影响性能。而现只要在父元素注册事件监听器,等待li事件触发后的冒泡阶段即可。
简单来说事件委托就是父元素监听子元素的冒泡事件。
2.2 ul、li场景示例
Div容器包含了多个li子元素,在Div容器注册事件委托。
HTML代码:
<div id="div">
<ul id="ul" >
<li data-key="北京">北京</li>
<li data-key="上海">上海</li>
<li data-key="杭州">杭州</li>
</ul>
</div>
JS代码:
document.getElementById('div').addEventListener('click',function(e){
var value=e.target.attributes['data-key'].value; // 获取目标阶段元素的'data-key'属性的值
console.log(value);
});
2.3 JQuery的事件委托
在JQuery中,父元素可调用delegate()、on()作为事件委托使用。
2.3.1 delegate()
语法:$('父元素').delegate( selector [, eventType] [, eventData], handler )
参数:
①selector {string} :子元素的选择器、
②eventType {eventType} 可选 :触发的事件类型。如:click。
③eventData {object} 可选 :触发事件时event.data指向的值。
④handler {function} :事件注册的处理程序。
示例:
$('#div').delegate('li', 'click', function() {
var v = $(this).data('key');
console.log(v);
});
2.3.2 on()
说明:JQuery1.7版本开始时,推荐on()代替delegate()方法。
语法:$('元素').on( events [, selector ] [, data ], handler )
参数:
①events {string} :一个或多个事件名称。
②selector {string} 可选 :子元素选择器。若无此值,表示元素注册本身的事件。若含有此值,表示只有子元素的事件触发,才会触发注册的事件。
③data {object} 可选 :触发事件时的event.data指向的值。
④handler {function} :事件注册的处理程序。
示例:
$('#div').on('click','li',function(e) {
var v = $(this).data('key');
console.log(v);
});