jQuery 事件 API 經歷了 bind → live → delegate → on/off 的演進。雖然舊 API 已棄用/移除,但理解其底層機制(直接綁定 vs 事件委派)對編寫高效能、無記憶體洩漏、支援動態元素的現代代碼至關重要。本文以實例剖析四種方式的異同,確立 on/off 統一規範。
DOM 事件流:捕獲 → 目標 → 冒泡。事件委派利用冒泡階段,將監聽器綁定在**祖先元素**上,當後代元素觸發事件冒泡上來時,透過 event.target 識別真正觸發源,執行對應處理邏輯。
優勢: 動態新增的子元素自動生效、減少監聽器數量 (節省記憶體)、避免頻繁綁定/解綁。
$('#myol li').bind('click', getHtml); // 逐個綁定現有 li
// 內部實現:this.on(types, null, data, fn)
$('a').click(fn) 等同 bind('click', fn)。$('#myol li').live('click', getHtml);
// 內部:$(this.context).on(types, this.selector, data, fn) → $(document).on(...)
document (或 context) 上,所有符合選擇器的元素 (含未來新增) 觸發事件冒泡到 document 時處理。stopPropagation() 會阻斷委派。$(document).find(selector) 需在文檔中可查到,chaining 後的選擇器可能失效。$('#myol').delegate('li', 'click', getHtml); // 綁在 ol 上
// 內部:this.on(types, selector, data, fn)
#myol),冒泡路徑短,效能優於 live。on,建議直接用 on。// 直接綁定 (相當於 bind)
$('#myol li').on('click', getHtml); // selector 省略
// 事件委派 (相當於 delegate)
$('#myol').on('click', 'li', getHtml); // selector 作為第 2 參數
// 多事件、命名空間、傳遞數據
$('#myol').on('click mouseenter', 'li', { foo: 'bar' }, function(e) {
console.log(e.data.foo); // 'bar'
console.log(e.currentTarget); // 委派目標 (#myol)
console.log(e.target); // 真正觸發元素 (li)
console.log(this); // 同 e.target (DOM 元素)
});
| 綁定方法 | 解綁方法 | 備註 |
|---|---|---|
bind | unbind | 棄用 |
live | die | 已移除 |
delegate | undelegate | 別名 |
on | off | **唯一推薦** |
// 解綁示例
$('#myol').off('click', 'li', getHtml); // 解綁特定委派
$('#myol li').off('click'); // 解綁直接綁定
$('#myol').off('click.myNamespace'); // 按命名空間解綁
$('#myol').off(); // 解綁該元素所有事件
$(selector).on(event, handler) 簡單直觀。$(stableParent).on(event, dynamicSelector, handler)。tbody, ul, #list-container)。避免綁在 document 或 body。off()** 清理委派監聽器。on() 同一事件,導致疊加觸發。$.noop 或具名函數而非匿名函數,便於精準 off(fn)。// 現代原生事件委派
document.querySelector('#stable-parent').addEventListener('click', function(e) {
const target = e.target.closest('.dynamic-child');
if (target && this.contains(target)) {
// target 即觸發元素,this 是委派容器
handler.call(target, e);
}
}, { passive: true }); // 被動監聽器提升滾動效能
原生 EventTarget.addEventListener 支援 { once: true, passive: true, capture: false } 選項,配合 AbortController 可實現更優雅的解綁。
jQuery 事件 API 的演進史,本質上是**從「直接綁定」向「事件委派」最佳實踐的標準化過程**。on/off 是唯一需掌握的 API,它統一了直接綁定 (selector 省略) 與委派綁定 (selector 提供) 的介面。新專案建議直接使用原生 addEventListener + 委派模式,僅在維護舊 jQuery 代碼時理解歷史 API 差異。核心心法:**委派給穩定的最近祖先,用最近的選擇器匹配動態目標,及時清理**。