一、事件冒泡和事件捕获的区别
- 事件冒泡:目标元素事件先触发,然后父元素事件触发
- 事件捕获:父元素事件先触发,然后目标元素事件触发
事件执行顺序是:
- 先事件捕获(从 Windows -> document 依次往下)
- 再是目标事件处理
- 最后是事件冒泡
addEventListener() 第三个参数为 false 事件触发顺序是冒泡顺序,true 为捕获顺序,默认为 false(这个冒泡和捕获是说当子元素被点击时,我是先执行还是后执行)。
<div class="parent">
<div class="child"></div>
</div>
<script>
window.onload = function() {
const $ = function(el) {
return document.querySelector(el);
};
// 冒泡:child parent
$(".parent").addEventListener("click", function() {
console.log("parent");
}, false);
// 捕获:parent child
$(".parent").addEventListener("click", function() {
console.log("parent");
}, true);
// 目标元素
$(".child").addEventListener("click", function() {
console.log("child");
}, false);
};
</script>
二、事件委托
事件委托(即事件代理),是利用事件冒泡原理,把事件监听绑定在元素的父级上。当元素被点击时,父级上绑定的点击事件就会被触发,事件触发函数里通过判断 e.target 上的 data-name 或 class 等标识来执行不同的逻辑。
优点
- 减少事件注册,节省内存。比如,在table上代理所有td的click事件。在ul上代理所有li的click事件。
- 简化了dom节点更新时,相应事件的更新。比如不用在新添加的li上绑定click事件。当删除某个li时,不用移解绑上面的click事件。
缺点
- 事件委托基于冒泡,对于不冒泡的事件不支持。
- 层级过多,冒泡过程中,可能会被某层阻止掉(event.stopPropagation)。
- 理论上委托会导致浏览器频繁调用处理函数,虽然很可能不需要处理。所以建议就近委托,比如在table上代理td,而不是在document上代理td。
- 把所有事件都用代理就可能会出现事件误判。比如,在document中代理了所有button的click事件,另外的人在引用改js时,可能不知道,造成单击button触发了两个click事件。
使用场景
1. 比如我们有个ul,ul里有99个li,我们不用为每个li绑定事件,我们只需要给ul绑定一个事件就可以了。如果li里面有子元素,我们需要使用递归调用:
ul.addEventListener("click", function(e) {
var target = e.target;
while (target.nodeName !== "ul") {
if (target.nodeName.toLocaleLowerCase() == "li") {
console.log(target.id);
break;
}
target = target.parentNode;
}
}, false);
2. React的事件都是代理到 document 上的。