前言
事件委託(Event Delegation) 是一種透過將事件監聽器繫結到父元素,而不是直接繫結到每個子元素上的技術。這樣可以減少事件監聽器的數量,提升效能,並使得對動態新增或移除的元素更容易進行事件處理。
事件冒泡和事件捕獲
事件冒泡:從裡往外
<div id="parent" style="padding: 50px; background-color: lightblue">
Parent Div
<button id="child" style="padding: 20px">Child Button</button>
</div>
<script>
const parentDiv = document.getElementById("parent");
const childButton = document.getElementById("child");
parentDiv.addEventListener(
"click",
event => {
console.log("Parent Div Clicked (Capturing)");
}
);
childButton.addEventListener("click", () => {
console.log("Button Clicked");
});
</script>
事件捕獲:從外往裡
const parentDiv = document.getElementById("parent");
const childButton = document.getElementById("child");
parentDiv.addEventListener(
"click",
event => {
console.log("Parent Div Clicked (Capturing)");
},
true
); // 捕獲階段處理
childButton.addEventListener("click", () => {
console.log("Button Clicked");
});
事件委託
事件委託主要是依賴於事件冒泡(事件從最深層的子元素冒泡到父元素)。透過將事件監聽器繫結到父元素,當子元素觸發事件時,事件會冒泡到父元素並在父元素上捕捉到事件。我們可以透過 event.target 來判斷事件最初是由哪個子元素觸發的。
事件監聽器繫結到 li 的父元素 ul 身上,點選其子元素 li,就可以觸發點選事件。點選子元素會依次往上冒泡,直到 ul 的事件監聽器捕獲到點選事件。
<button id="btn">點選新增標籤項</button>
<ul id="ul">
<li>標籤項</li>
<li>標籤項</li>
<li>標籤項</li>
</ul>
<script>
const btn = document.querySelector("btn");
const ul = document.querySelector("#ul");
ul.addEventListener("click", function (e) {
console.log(e.target);
});
</script>
Vue 列表迴圈效能最佳化
當初透過 B站 網上影片學習 Vue,對於這類需求影片中直接把事件繫結給 li,直到了解到事件委託和事件冒泡,才知道這樣做對效能有很大影響。
在 v-for
迴圈中將索引 index 繫結到 <li>
元素的 data-
屬性中,然後在事件委託的處理函式中透過 event.target
獲取它。
<template>
<ul @click="handleClick">
<li v-for="(item, index) in items" :key="index" :data-index="index">
{{ item }}
</li>
</ul>
</template>
<script>
export default {
data() {
return {
items: ['Item 1', 'Item 2', 'Item 3'],
};
},
methods: {
handleClick(event) {
// 判斷點選的目標是否為 <li> 元素
if (event.target.tagName === 'LI') {
// 透過自定義屬性 data-index 獲取對應的索引
const index = event.target.dataset.index;
console.log('Clicked item index:', index);
console.log('Clicked item value:', this.items[index]);
}
}
}
}
</script>