LearnWeb-Taiwan / Meetups

聚會相關資訊
MIT License
6 stars 2 forks source link

[191117]-Sapling #5

Open saplingt opened 4 years ago

saplingt commented 4 years ago

今天複習了 JavaScript 選取器, 阻止冒泡


當天還有什麼較不瞭解的地方想問的?

shawnlin0201 commented 4 years ago

@saplingt Hi

要瞭解 stopPropagation( ) 函式,主要要瞭解 JavaScript 事件的相關概念 :D

JavaScript 事件

JavaScript 事件在傳遞事件時,會遵循 DOM 上的事件傳遞機制,而與事件傳遞機制有關的 JavaScript API 有:

addEventListener( )

addEventListener( ) 的概念很簡單,就是增加一個監聽器給 DOM 上的元素。

第一個傳入的參數是填入監聽事件的名稱,例如要監聽滑鼠動作即為 addEventListener('click')

第二個傳入的參數則是一個 Callback Function,意思即為當監聽到第一個參數所監聽的行為後,他要去執行裡面的函式,而這裡通常我們會放入傳送參數來捕捉 Event 事件相關資訊。

window.addEventListener('click', function (e) {
    console.log(e) // 這裡就會回傳事件相關的資訊
})

第三個傳入的參數是一個 boolean 值,當傳入 true 時,就會將監聽器加入到 capturing 的階段,若是 false ( 預設值 ),則是將監聽器加入到 bubbling 的階段。

捕獲與冒泡 Capturing & Bubbling

以 W3 對於 event flow 事件流程的說明圖解釋 capturing 與 bubbling 的話:

範例

假設我們今天有一個頁面:

<html>
    <ul id="navbar-list">
        <li id="navbar-item">
            <a id="navbar-link"> test </a>
        </li>
    </ul>
</html>

我們透過監聽器去捕捉事件的傳遞過程:

let navbarList = document.getElementById('navbar-list')
let navbarItem = document.getElementById('navbar-item')
let navbarLink = document.getElementById('navbar-link')

navbarList.addEventListener('click', function (e) {
    console.log('list-capturing',e.eventPhase)
},true)

navbarList.addEventListener('click', function (e) {
    console.log('list-bubbling',e.eventPhase)
},false)

navbarItem.addEventListener('click', function (e) {
    console.log('item-capturing',e.eventPhase)
},true)

navbarItem.addEventListener('click', function (e) {
    console.log('item-bubbling',e.eventPhase)
},false)

navbarLink.addEventListener('click', function (e) {
    console.log('link-capturing',e.eventPhase)
},true)

navbarLink.addEventListener('click', function (e) {
    console.log('link-bubbling',e.eventPhase)
},false)

然後我點下了 <a id="navbar-link"> test </a> 時,最後會得到下面的結果。

list-capturing 1      // 捕獲
item-capturing 1   // 捕獲
link-capturing 2    // 捕獲
link-bubbling 2     // 冒泡
item-bubbling 3   // 冒泡
list-bubbling 3      // 冒泡

從結果中我們可以清楚看到事件傳遞的順序如捕獲冒泡機制一樣呈現。

這邊有個很大的問題在於冒泡階段,因為我們通常是採用預設值 false 來監聽元素 被冒泡 的時候要做什麼事情。

問題在於我明明點了是 <a> 元素,但是其他元素的冒泡過程中監聽器也被啟動了,假設我原先替 <li> 也放了一個監聽器來捕捉,那麼 <li> 的監聽器也會隨著 <a> 被點擊而跟著觸發。

因此,stopPropagation( ) 便是用來阻止事件繼續傳遞下去。

我們利用上面的 JavaScript 範例更改一下:

let navbarList = document.getElementById('navbar-list')
let navbarItem = document.getElementById('navbar-item')
let navbarLink = document.getElementById('navbar-link')

navbarList.addEventListener('click', function (e) {
    console.log('list-capturing',e.eventPhase)
},true)

navbarList.addEventListener('click', function (e) {
    console.log('list-bubbling',e.eventPhase)
},false)

navbarItem.addEventListener('click', function (e) {
    console.log('item-capturing',e.eventPhase)
},true)

navbarItem.addEventListener('click', function (e) {
    console.log('item-bubbling',e.eventPhase)
},false)

navbarLink.addEventListener('click', function (e) {
    console.log('link-capturing',e.eventPhase)
},true)

navbarLink.addEventListener('click', function (e) {
    console.log('link-bubbling',e.eventPhase)
        e.stopPropagation()                                              // 在這裡,我們要告訴事件不要傳遞下去
},false)

同樣再次點擊 標籤,此時秀出的結果為:

list-capturing 1      // 捕獲
item-capturing 1   // 捕獲
link-capturing 2    // 捕獲
link-bubbling 2     // 冒泡

可以看到原先會繼續傳遞下去的冒泡事件就停在被阻止的地方。

若我們以實務上的監聽器做法 (不加第三個傳送參數,只單純捕捉冒泡事件)

navbarList.addEventListener('click', function (e) {
    console.log('list-bubbling',e.eventPhase)
})

navbarItem.addEventListener('click', function (e) {
    console.log('item-bubbling',e.eventPhase)
})

navbarLink.addEventListener('click', function (e) {
    console.log('link-bubbling',e.eventPhase)
    e.stopPropagation()
})

再次點擊 元素,則 console.log 結果為:

link-bubbling 2

這個結果顯示,我們已經透過阻止 DOM 事件傳遞機制,來達成監聽到特定元素目標的做法,此時其他元素也仍保持監聽並不受影響。

這個完整的脈絡就是使用 stopPropagation() 的原因,而 stopPropagation() 並不需要傳入參數。因為他啟用當下效果是阻止監聽目標元素不繼續傳遞事件。

preventDefault( )

preventDefault( ) 用法則更簡單易懂,他的用意單純就是阻止當前瀏覽器預設行為。 例如當 <a herf="#">Link</a> 被點擊時,事件傳遞當目標元素是的當下我們預期會點開一個連結,而若用監聽器監聽該目標,並且使用 preventDefault( ) 的話,那麼就會阻止瀏覽器點開一個連結。

以上是 JavaScript 傳遞事件完整的脈絡,若還有問題或不清楚的地方再麻煩你回復囉!