阿川私房教材:
學 JavaScript 前端,帶作品集去面試!

63 個專案實戰,寫出作品集,讓面試官眼前一亮!

立即開始免費試讀!

什麼是事件代理?

事件代理是一種技術,您可以將單一事件監聽器附加到父元素上,以管理其子元素的事件。與其對每個子元素添加個別監聽器,不如讓父元素捕捉冒泡的事件並識別互動的來源。

它是如何運作的?

事件代理依賴兩個關鍵的 JavaScript 機制:

  • 事件冒泡: 事件從目標元素傳播到 DOM 樹的根部。

  • event.target: 確定事件的來源元素。

事件代理的優勢

<table style="margin: 0; padding: 0;">
<thead>
<tr>
<th>特性</th>
<th>說明</th>
</tr>
</thead>
<tbody>
<tr>
<td><b>效能</b></td>
<td>減少事件監聽器的數量,節省記憶體並提高效率。</td>
</tr>
<tr>
<td><b>控制機制</b></td>
<td>自動管理動態新增的元素,而無需額外的監聽器。</td>
</tr>
<tr>
<td><b>記憶體管理</b></td>
<td>將事件處理邏輯集中在較少的代碼區域。</td>
</tr>
<tr>
<td><b>常見使用案例</b></td>
<td>在現代瀏覽器中普遍支持。</td>
</tr>
</tbody>
</table>

深入了解事件傳播

JavaScript 事件通過 DOM 遵循可預測的生命週期。理解這些階段對掌握代理至關重要:

1. 捕獲階段: 事件從根部開始向下傳遞到目標元素。
2. 目標階段: 事件在目標元素上激活。
3. 冒泡階段: 事件向上傳播回根部。

事件代理主要在冒泡階段運作。

代碼範例:事件代理的實踐

場景 1:管理列表的點擊事件
與其對每個列表項添加監聽器:

const ul = document.querySelector("ul");
ul.addEventListener("click", (event) => {
    if (event.target.tagName === "LI") {
        console.log("點擊的項目:", event.target.textContent);
    }
});

這個單一的監聽器管理所有 li 元素,甚至是那些動態添加的:

const ul = document.querySelector("ul");
ul.innerHTML += "<li>新項目</li>"; // 不需要新的監聽器。

<br>
場景 2:委派多種類型的事件
將事件代理與事件類型檢查結合使用:

document.querySelector("#container").addEventListener("click", (event) => {
    if (event.target.matches(".button")) {
        console.log("按鈕被點擊");
    } else if (event.target.matches(".link")) {
        console.log("連結被點擊");
    }
});

<br>
場景 3:使用代理處理表單

document.querySelector("#form").addEventListener("input", (event) => {
    if (event.target.matches("input[name='email']")) {
        console.log("電子郵件已更新:", event.target.value);
    } else if (event.target.matches("input[name='password']")) {
        console.log("密碼已更新。");
    }
});

這種方法確保任何動態添加的輸入字段自動得到處理。

事件代理的最佳實踐

1. 使用特定選擇器: 避免廣泛的匹配,以防止意外行為。使用 event.target.matches()event.target.closest()
2. 避免過度代理: 對父元素過度委派太多事件可能會變得低效,如果父元素包含眾多子元素。
3. 優化條件邏輯: 組織您的條件以最小化不必要的檢查。
4. 限制或防抖事件: 對於 scrollresize 等事件,使用限制策略以提高效能:

function throttle(callback, delay) {
    let lastTime = 0;
    return function (...args) {
        const now = Date.now();
        if (now - lastTime >= delay) {
            callback(...args);
            lastTime = now;
        }
    };
}
document.addEventListener("scroll", throttle(() => console.log("已滾動!"), 200));

事件代理與直接事件處理

方面 直接事件處理 事件代理
設定複雜性 需要多個監聽器。 單一監聽器處理多個事件。
動態元素 需要手動重新附加。 自動支持。
在大型 DOM 中的效能 隨著監聽器數量的增長而降低。 通過集中監聽器來實現效能。
可維護性 在多個地方散佈邏輯。 集中且乾淨。

框架中的事件代理

React
雖然 React 抽象了 DOM 操作,但您可以在合成事件中看到代理的等效物。React 使用一個根監聽器來管理其虛擬 DOM 中的所有事件。

jQuery
jQuery 的 .on() 方法簡化了代理:

$(document).on("click", ".dynamic-button", function () {
    console.log("按鈕被點擊:", $(this).data("id"));
});

事件代理中的常見陷阱

1. 意外匹配
確保您的選擇器不會意外地匹配不相關的元素。使用特定的選擇器或 event.target.closest()

2. 防止事件冒泡
在某些情況下,您可能需要為特定元素停止冒泡:

document.querySelector("#container").addEventListener("click", (event) => {
    if (event.target.matches(".prevent-bubble")) {
        event.stopPropagation();
    }
});

效能考量

1. 基準測試
事件代理在大型 DOM 中減少了記憶體使用,但如果父元素處理過多事件,可能會引入延遲。

2. 開發者工具
使用瀏覽器開發者工具分析已附加的監聽器(在 Chrome 的控制台中使用 getEventListeners):

getEventListeners(document.querySelector("#parent"))

提示與技巧

  • 模擬非冒泡事件的代理:
    某些事件,如 focusblur,不會冒泡。改用 focusinfocusout
document.addEventListener("focusin", (event) => {
    console.log("聚焦到:", event.target);
});
  • 在根級別附加代理:
    對於 SPA 或動態內容的最大靈活性,將監聽器附加到 document

結論

JavaScript 事件代理 是一種關鍵的優化策略,能有效地擴展到互動式應用中。透過集中事件處理、減少記憶體使用和提高可維護性,它使開發人員能夠構建穩健且高效的網頁應用程序。


我的網站:https://shafayet.zya.me


給你的一個迷因(也許會有共鳴...)🙃🙃


原文出處:https://dev.to/shafayeat/mastering-javascript-event-delegation-3k2k


共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。

阿川私房教材:
學 JavaScript 前端,帶作品集去面試!

63 個專案實戰,寫出作品集,讓面試官眼前一亮!

立即開始免費試讀!