iT邦幫忙

2021 iThome 鐵人賽

DAY 25
0
自我挑戰組

JS30 學習日記系列 第 25

Day 25 - Event Capture, Propagation, Bubbling and Once

前言

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 學習日記30

尚未有邦友留言

立即登入留言