本系列來到了 JavaScript 相關的主題,預計會在這逐步前進好一陣子,讓我們從較簡單的主題開始吧;今天就先來聊聊瀏覽器中的事件代理是怎麼一回事。
本系列文已經重新編校彙整編輯成冊,並正式出版囉!
《前端三十:從 HTML 到瀏覽器渲染的前端開發者必備心法》好評販售中!
喜歡我文章內容的讀者們,歡迎您 前往購買 支持!
在網頁中,如果想與使用者進行「互動」,勢必需要透過一些方法捕捉使用者做了什麼。當然,瀏覽器開發者們早已依據 W3C 的事件規範,幫我們實作好了底層的邏輯,我們只需要透過 Web API 中的 DOM Event,針對想監聽的 DOM 元素 & 事件,註冊事件監聽器(Event Listener)便可以輕鬆掌握使用者在網頁上的一舉一動。
前面剛提到了「事件監聽器」,開發者可以在想要監聽事件的 DOM 元素上,透過 addEventListener 註冊監聽器。例如:
document.querySelector('#id').addEventListener('click', clickHandler)
當使用者對 #id
元素點擊時,便會觸發 clickHandler
,並會傳入一個事件物件,內容包含事件傳遞過程中的紀錄,例如目標元素、當前元素、傳遞階段等等。開發者便可以從中取得所需要的資料,並對資料做你想做的事情。
一個現代網站有大量的使用者互動是稀鬆平常的事情,若是透過事件監聽一個一個寫,那豈不是到處都要註冊事件監聽?除了效能很差,寫起來也很麻煩;這也就是「事件代理」的重要性了!
不過在聊到事件代理之前,我們需要先理解 DOM Tree 上的事件傳遞機制是如何運作的。
可以參考這張 W3C 所定義的 Event Flow 圖:
規範中定義了事件傳遞的三個階段:
如圖所示,當使用者觸發一個 DOM 元素的事件時,首先會進入 「捕獲階段(Capture Phase)」,由根結點逐步向事件目標傳遞;到達目標後則進入「目標階段(Target Phase)」,接著就開始折返,進入向根結點傳遞的「冒泡階段(Bubbling Phase)」。
在開發者使用 addEventListener
註冊事件監聽器時,可以透過傳遞第三個參數,指定此事件監聽要在什麼階段觸發:
elem.addEventListener('click', eventHandler) // 未指定,預設為冒泡
elem.addEventListener('click', eventHandler, false) // 冒泡
elem.addEventListener('click', eventHandler, true) // 捕獲
elem.addEventListener('click', eventHandler, {
capture: true // 是否為捕獲。IE、Edge 不支援。 其他物件屬性請參考 MDN
})
透過簡單的來回傳遞,這樣開發者就能更精準的控制觸發的時機了!
好啦,我們終於聊到了事件代理。由於事件傳遞的機制,子元素的事件在傳遞過程中勢必會經過他的父元素;而事件代理,顧名思義就是將子元素事件監聽器交由父元素代理。
什麼意思呢?我們直接看個簡單的對照範例:
差異就只在事件監聽的目標元素。
無事件代理的版本中,事件監聽器註冊在每一個 li
上,當數量越來越多,瀏覽器也就建立了越來越多的監聽器,無形中對效能有很大的傷害;反之,在有事件代理的版本則將事件監聽器註冊在外層的 ul
上,無論內容有多少,瀏覽器都只需要負擔一組事件監聽器的消耗。
現在軟體開發中,使用套件是稀鬆平常的;在 DOM 事件處理的這部分,jQuery & Vue 都將原生的事件監聽器做了包裝,方便開發者快速設定、取用,甚至會自動幫你移除無用的事件監聽。
但在 React 中,React DOM 上直接註冊的事件監聽器,其實監聽的是 React 額外封裝過的 React DOM Event,並將全部事件代理到 document 上,與原生事件有蠻大的不同;特別是在執行順序上,若有混用 React DOM Even tListener 及原生的 addEventListener
,事件監聽器之間的執行順序很有可能會不如預期,寫 React 的朋友要特別注意喔!
有興趣深入研究的朋友可以從 這邊 開始查找 React 關於事件處理的 Code。
前端工程師在製作網站時,活用大量的互動式設計讓網站更吸引人的同時,也要記得多少活用事件代理的概念,更優雅、乾淨、高效的處理互動事件吧!另外,筆者先前也寫過一篇討論 DOM 事件傳遞的相關文章,並遇到了一個蠻奇妙的 Bug,有興趣的朋友歡迎移駕 我的部落格。
以上就是今天的分享,若讀者您對文中內容有任何想法,都歡迎你於底下討論區回應;或是參考 本系列文其他文章,讓我們一起繼續這趟每天逐步變強的旅程!
筆者
Gary
半路出家網站工程師;半生熟的前端加上一點點的後端。
喜歡音樂,喜歡學習、分享,也喜歡當個遊戲宅。相信一切安排都是最好的路。