前言
事件委托(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>