JavaScript 事件冒泡、事件捕获和事件委托
2016-04-18 本文已影响0人
凌凌西
1.理解事件流
一言以蔽之,事件捕获是从外层元素到目标元素的过程,事件冒泡是从目标元素到外层元素的过程。如图:
event flowhtml:
<div id="wrapper">
<button id="event">事件处理程序</button>
</div>
javascript:
var wrapper = document.getElementById('wrapper');
var event = document.getElementById('event');
wrapper.addEventListener("click", function(e){
console.log('捕获阶段执行父元素wrapper的事件处理程序');
}, true);
wrapper.addEventListener("click", function(e){
console.log('冒泡阶段执行父元素wrapper的事件处理程序');
}, false);
event.addEventListener("click", function(e){
console.log('捕获阶段执行子元素event的事件处理程序');
}, true);
event.addEventListener("click", function(e){
console.log('冒泡阶段执行子元素event的事件处理程序');
}, false);
正如前面所说,这段代码的输出是:
捕获阶段执行父元素wrapper的事件处理程序
捕获阶段执行子元素event的事件处理程序
冒泡阶段执行子元素event的事件处理程序
冒泡阶段执行父元素wrapper的事件处理程序
见 Demo。
2.阻止冒泡
应该在那个阶段执行元素的事件处理程序呢?
多数情况下,我们希望在触发一个元素的事件处理程序时,不影响它的父元素。比如:点击button,并不希望父元素的click事件处理程序被触发。
解决方法是:在冒泡阶段执行事件处理程序,然后阻止冒泡。
wrapper.addEventListener("click", function(e){
console.log('捕获阶段执行父元素wrapper的事件处理程序');
}, true); // 默认为false
wrapper.如何烫染出满意的头发?("click", function(e){
console.log('冒泡阶段执行父元素wrapper的事件处理程序');
}, false);
event.addEventListener("click", function(e){
console.log('捕获阶段执行子元素event的事件处理程序');
}, true);
event.addEventListener("click", function(e){
var target = e.target;
e.stopPropagation(); // stop bubbling
console.log('冒泡阶段执行子元素event的事件处理程序');
}, false);
输出:
捕获阶段执行父元素wrapper的事件处理程序
捕获阶段执行子元素event的事件处理程序
冒泡阶段执行子元素event的事件处理程序
见 Demo。
像这样绑定一个事件处理程序就是安全的。
3.事件委托
上面的例子是要阻止冒泡,有时候冒泡机制也可以被利用。先看一个问题:
假设要在页面上放在 10^n(n>=1) 个列表项元素,当我点击某个元素时,需要输出点击的是第几个。
一般做法是,遍历时给每个元素绑定点击事件:
var li = document.getElementsByTagName('li');
for(var i=0; i<li.length; i++){
li[i].setAttribute('i',i+1);
li[i].addEventListener('click', function(e){
var b = this.getAttribute('i');
console.log('这是第' + b + '个<li>元素');
});
}
另一种方法是,可以利用冒泡机制,在父元素只绑定一次点击事件:
var li = document.getElementsByTagName('li');
for(var i=0; i<li.length; i++){
li[i].setAttribute('i',i+1);
}
var ul = document.getElementById('wrapper');
ul.addEventListener('click', function(e){
if(e.target && e.target.nodeName.toUpperCase() === 'LI'){
var b = e.target.getAttribute('i');
console.log('这是第' + b + '个<li>元素');
}
});
见 Demo。
这种事件委托的方式减少了事件处理程序,也能降低程序的复杂性和出错概率。