一、事件冒泡和事件捕獲的區別
- 事件冒泡:目標元素事件先觸發,然後父元素事件觸發
- 事件捕獲:父元素事件先觸發,然後目標元素事件觸發
事件執行順序是:
- 先事件捕獲(從 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 上的。