iT邦幫忙

2024 iThome 鐵人賽

DAY 25
0

Event Capture, Propagation, Bubbling and Once

這次是介紹事件捕捉、事件冒泡、停止冒泡、事件監聽。當你很清楚這些事件的時候,開發時就比較不會踩雷,然後卻不知道為什麼會這樣。其實概念不困難,實際看範例就懂了~

個人codepen

技巧點

1. Event Bubbling事件冒泡

以下是html結構

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

再來是監聽點擊事件

const divs = document.querySelectorAll("div");

function logTxt(e) {
  console.log(this.classList.value);
}

divs.forEach((item) => item.addEventListener("click", logTxt));

上面的監聽點擊,預設是冒泡行為,當你點擊最內層的three時,會依序觸發three、two、one,由內往外觸發點擊事件。


那如果你只希望事件停留在最內層three的時候,就要加上stopPropagation()。如下,點擊事件就不會向上傳遞,只停留在three。


function logTxt(e) {
  e.stopPropagation();
  console.log(this.classList.value);
}

2. Event Capture事件捕捉

同樣的html結構

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

於綁定監聽事件時,設定事件捕捉。

const divs = document.querySelectorAll("div");

function logTxt(e) {
  e.stopPropagation();
  console.log(this.classList.value);
}

divs.forEach((item) => item.addEventListener("click", logTxt, true));

跟Bubbling事件冒泡差別就在於addEventListener的第三個參數true。第三個參數本身沒有代入時,就預設是false,因此可省略。你要改成事件捕捉,就要設定第三個參數為true。

此時當你點擊three的時候,點擊事件會從最外層的one開始向內傳遞。會依序log出one、two、three的文字。

3. addEventListener(type, listener, options)監聽事件的第三個參數

第三個參數為一個物件,可以代入幾個值,capture、once、passive等...

  • once,預設的情況下,監聽器只要事件有觸發就會作用。設定once後,此事件在觸發第一次後,就會自動移除監聽。等於是在觸發之後,removeEventListener的概念。
divs.forEach((item) => item.addEventListener("click", logTxt, {
  once: true,
}));

  • capture是否使用事件捕捉,預設為false。
divs.forEach((item) => item.addEventListener("click", logTxt, {
  once: true,
  capture: true,
}));

如果你只需要用事件捕捉,不需要用once的話,就可以簡潔的直接帶入true。

divs.forEach((item) => item.addEventListener("click", logTxt, true));

  • passive,是否禁用preventDefault(),預設為false。當你要阻止預設的事件發生時,通常就會用preventDefault()。像是用了form標籤,會有預設的送出行為。看你需求決定是否使用preventDefault。
<form action="">
  <button>submit</button>
</form>

const button = document.querySelector("button");
button.addEventListener('click', (e) => {
  e.preventDefault();
  console.log("submit!!!");
})

如果今天設置了passive為true,即使你寫了preventDefault也不會有作用。原生預設的form事件還是會發生。而且控制台會報錯,

button.addEventListener('click', (e) => {
  e.preventDefault();
  console.log("submit!!!");
}, {
  passive: true,
})

https://ithelp.ithome.com.tw/upload/images/20241004/20169174yEsUnfaJDy.png


MDN文件中,有提到某一些的事件,像是wheel、mousewheel、touchstart 、touchmove,本身的passive就是true。讓瀏覽器的預設行為可以繼續進行,以提高性能。
因為瀏覽器在不確定你是否要執行preventDefault的狀況下,會有延遲來等待結果,進而造成畫面有點卡頓。目前,瀏覽器的預設passive都是true。

心得

重新看了一次MDN文件,發現真的寫很詳細,而且連範例都有。不過你如果切換成中文版的,會發現資料變超少。所以只能乖乖看英文,看不懂就去翻譯。難怪人家都說要看原文文件。不然資料都掉一半...


上一篇
Sticky Nav 黏貼導覽列
下一篇
Stripe Follow Along Dropdown
系列文
鱷魚帶我練習JavaScript之個人練功坊29
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言