iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 25
1
Modern Web

JS30 錄系列 第 25

Day 25 - Event Flow - Bubble and Capture

任務目標

明白事件同時觸發時的先後順序。

重點整理

先了解可能同時觸發事件的狀況,下面是爺元素、父元素、子元素,一個包一個,祖孫三代齊聚一堂。加了一些 CSS ,因此實際呈現後會像這樣

<div class="one">
  <div class="two">
    <div class="three">
    </div>
  </div>
</div>

將其全部添加監聽器,監聽點擊事件,一旦觸發會回饋觸發元素的類別名稱。

const divs = document.querySelectorAll('div');
  function logText(e) {
    console.log(this.classList.value);
  }

divs.forEach(div => div.addEventListener('click', logText);

因為元素們互相重疊,因此點擊最裡面的元素,會同時觸發三個事件。問題來了!究竟哪個事件會先被觸發呢?

答案是 threetwoone!猜對了嗎?這是由於事件觸發順序的預設在 冒泡階段(Bubbling)觸發。

是否一頭霧水?首先來看看瀏覽器是如何捕捉事件觸發的吧。

圖片取自 W3C 文件

DOM Event Flow

上圖解釋了觸發事件時,事件的觸發流程,這個流程稱為事件流(Event Flow)。觸發事件的最終目標稱為 EventTarget , 在碰觸到 EventTarget 前,瀏覽器會從最外層元素開始,搜尋每個 EventTarget 的外層元素是否有事件觸發,這個搜尋一直到 EventTarget 為止。這個階段稱作 Capture Phase ,可以想像一直向下鎖定目標那種感覺。

接著碰觸到最終目標,這個階段稱作 Target Phase

最後階段稱作 Bubble Phase ,會由最終目標開始往外層元素逐層搜索是否有事件觸發,直至最外層元素為止,可以想像泡泡從水底往上移動。

`EventTarget.addEventListener('eventType', callBack, [, options])

事件監聽器 addEventListener 的第三個參數為選用參數,可以用來決定事件要在什麼階段觸發。預設是 false ,表示不希望在 Capture 階段觸發,也就是說,事件會在 Bubble 冒泡階段觸發,所以依照示意圖,會從最底層元素開始往外觸發,因此先 three ,接著往父元素尋找,發現也有監聽事件,因此觸發 two ,以此類推最後 one

如果第三個參數輸入 true ,事件會在 Capture 階段觸發,因此由外而內尋找監聽器並觸發事件,得到的結果會是 onetwothree

第三個參數也能用物件的方式輸入,像這樣:

  divs.forEach(div => div.addEventListener('click', logText, {
    capture: false,
    // 下面可以設置別的選項
  }));

該種方式可以涵蓋其他選項,後續會提到。

有時候元素不可避免會重疊,各元素又有相對應的監聽事件,但我們基於某些操作可能只希望第一個元素觸發,此時可以使用 Event.stopPropagation() 來阻止事件觸發的傳遞。

例如下面程式碼這樣,就能在第一個事件觸發後,瀏覽器就停止 Event Flow 的前進。因此如果第三個參數 false 的狀態,就只會顯示 three ,反之則為 one 。不會有其他兩個數字。

  function logText(e) {
    console.log(this.classList.value);
    e.stopPropagation(); // stop bubbling!
  }

最後介紹 once 這個選項,當第三個選項是物件時可以放進屬性內。

divs.forEach(div => div.addEventListener('click', logText, {
  once: true,
}));

這是個惡毒的屬性,會在事件觸發後直接將監聽器移除,因此事件只能觸發一次。如果我們想讓某個事件只執行一次,可以使用該屬性。

以上就是 JS30 第二十五篇!

Reference

EventTarget.addeventListener
完整程式碼


上一篇
Day 24 - Sticky Nav
下一篇
Day 26 - Follow Along Dropdown
系列文
JS30 錄30

尚未有邦友留言

立即登入留言