當使用者與瀏覽器互動時,會觸發各類不同的事件(event),例如常見的點擊(click)、滑動(scroll)。我們可以透過 JavaScript 的事件處理器(handler),來處理這些事件。讓我們能在事件觸發時,做出我們要的效果,例如點擊某個按鈕,觸發某個邏輯。
針對瀏覽器事件,最常見的考題之一,便是事件委派、事件捕獲、事件冒泡,是很常見的面試考題。以下將用第一人稱的擬答,來回答「請說明瀏覽器中的事件委派、捕獲、冒泡」這個問題。
事件委派
事件委派是當我們想要在一群子元素中,都加上同樣的事件監聽器與處理器時可以派上用場。當我們有許多相同元素,有相似的行為時,我們可以不用在每個元件都加上處理器,而是可以直接在父層加上處理器。這時透過 event.target
來得知實際上是哪一個元素發生事件,並處理該事件。
這種把監聽器與處理器裝在父層,然後委派給子元素,就是所謂的事件委派
。
這麼做的好處是,我們不用在每個元件,例如每個按鈕上都加上處理器,這可以減少記憶體消耗;這也讓我們的架構更彈性,可以隨時新增或移除元素。也可以寫比較少的程式碼,讓可閱讀性提升。
舉例來說 (編按:此例子來自 MDN),如果想要在一長串列表中的每個項目,都加上處理器,我們可以直接加在父層,不用每個子元素都加上,就算今天有上百上千個子元件都是。
|
|
|
|
事件捕獲
事件委派之所以能夠發生,是因為在背後的事件捕獲
與冒泡機制
。一般來說,當事件觸發時,會先進入捕獲階段,然後到達事件目標,接著才是冒泡階段。(建議在面試時,可以簡單手繪這張 W3C 的事件流程,會更加幫助說明唷!)
從上圖可得知,所謂的捕獲階段是指:當某個事件觸發時,例如使用者點了某個按鈕,此時由 DOM 樹的最上層 Window 一路往下,將事件傳遞下去並執行
。實際在程式碼上,需要在事件監聽器中,加入 {capture: true}
來開啟捕獲機制。
事件冒泡
冒泡階段則是比較常用的,跟捕獲階段相反,它是先在目標上執行事件處理器,接著傳遞到父層,再傳到祖父層,然後一路傳上去
。
|
|
以上面的例子來說(建議在面試時也可以簡單快速手寫這個例子,可以幫助說明),當我們在子層 <p>
裝一個 onclick
的處理器,點下去時,不僅該元素有跑出 alert
,其父層 <div>
的 onclick
也被觸發,然後祖父層 <form>
的 onclick
也接續被觸發。
這邊有個細節需要分別,在冒泡時的 this
不必然等於 event.target
,而是會等於 event.currentTarget
。換句話說,this
是正在執行的處理器 (會一直變成下一個);而 event.target
一直都會是真正變點擊的那個(在這邊就是最裡頭的子層)。
在實務上,我們有時候不想要冒泡,例如只想要子元素的事件被觸發,不想要父層的元素被觸發,避免干擾。這時候想要不發生冒泡,可以在處理器加上 event.stopPropagation()
(不過這個仍會讓該處理器執行,只是不會冒泡上去);如果連該處理器的其他事件類別都不想執行的話,可以用 event.stopImmediatePropagation()
。