iT邦幫忙

2021 iThome 鐵人賽

DAY 25
1
自我挑戰組

JS30 學習日記系列 第 25

Day 25 - Event Capture, Propagation, Bubbling and Once

  • 分享至 

  • xImage
  •  

前言

JS 30 是由加拿大的全端工程師 Wes Bos 免費提供的 JavaScript 簡單應用課程,課程主打 No FrameworksNo CompilersNo LibrariesNo Boilerplate 在30天的30部教學影片裡,建立30個JavaScript的有趣小東西。

另外,Wes Bos 也很無私地在 Github 上公開了所有 JS 30 課程的程式碼,有興趣的話可以去 fork 或下載。


本日目標

瞭解addEventListener中事件的捕捉、傳遞(Event Bubbling/Capturing)以及一次性的事件監聽(Once)。


解析程式碼

HTML 部分

建立三層的div做為測試event listener的物件。

div(.one) : 淡紫色
div(.two) : 淡粉色
div(.three) : 橘色

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

JS 部分

  • Event Bubbling

首先取得所有的div標籤,然後在每個div上都註冊cllick event listener並以logText()進行事件處理把div標籤的class屬性值印出來。

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

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

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

點擊最內層的div(橘色),console 印出的內容如下 :

由上面的結果,我們可以得知在 div(.three)捕捉到事件後,還會連帶向上觸發parentsevent handler,這種由底部向上傳遞觸發event handler的機制稱為event bubbling

傳遞順序 : div.three(橘色) -> div.two(淡粉色) -> div.one(淡紫色)

  • Event Capturing

前面的Event Bubbling是從觸發事件的element開始向外層的parent element進行事件傳遞,而Event Capturing則是從觸發事件的element的最外層parent element向內進行傳遞。

要做到這一點,我們就必須使用到addEvenListener的第三個參數Options Objectcapture屬性,這個屬性的預設值是false,我們只需要把它改成true即可。

/*上略...*/
divs.forEach(div => div.addEventListener('click',logText,{
    capture: true
}));

設定完後點擊div.three,console 印出結果如下 :

傳遞順序 : div.one(淡紫色) -> div.two(淡粉色) -> div.three(橘色)

capture : true,Event Capturing
capture : false,Event Bubbling

上方的Event Bubbling其實可被改寫如下 :

/*上略...*/
divs.forEach(div => div.addEventListener('click',logText,{
    capture: false //預設就是 false 可直接省略
}));
  • Event Propagation

那如果不想讓事件由內向外(bubbling)或由外向內(capturing)傳遞要怎麼辦呢? 我們可以在event handler裡面對Event呼叫stopPropagation(),讓事件不再繼續傳遞。

以阻止Event Bubbling為例,在event handler裡面,我們對event(e)呼叫stopPropagation()

/*上略...*/
function logText(e){
    console.log(this.classList.value);
    e.stopPropagation(); //stop bubbling or capturing
}

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

然後點擊div.three,console 印出結果如下 :

由上可知呼叫stopPropagation()後,事件就沒有繼續傳遞。

  • Event Once

addEvenListener的第三個參數Options Object的屬性除了capture之外,還有once這個屬性(預設是false),它可以用來指定是否在觸發一次事件處理後,就unbind event listener(讓事件監聽器失效)。

下面以按鈕作為例子 :

在網頁上放置一個button

<button>Button</button>

透過 JS 取得button標籤,然後為其註冊click event listener並指定Options Objectonce屬性為 true。

const button = document.querySelector('button');
const text = '努力は自分に裏切らない、梦想は裏切ります!努力は梦を実现することはできない!しかし、努力している事実は自分を慰めることができる。--大先生'
button.addEventListener('click',() => alert(text),{
    once: true
});

此時點擊button,視窗會顯示提示訊息,但若在未 reload 頁面的情況下二次點擊button,會發現視窗不再顯示提示訊息,也就是button失效了。

這個效果可以放在提交表單的按鈕上,用來防止使用者重複提交表單。

補充資料:

EventTarget.addEventListener()
Event.stopPropagation()
[教學] 瀏覽器事件:Event Bubbling, Event Capturing 及 Event Delegation

點擊檢視完整程式碼(Wes Bos)


上一篇
Day 24 - Sticky Nav
下一篇
Day 26 - Stripe Follow Along Dropdown Navigation [更新]
系列文
JS30 學習日記31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言