Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[191117]-Sapling #5

Open
saplingt opened this issue Nov 21, 2019 · 1 comment
Open

[191117]-Sapling #5

saplingt opened this issue Nov 21, 2019 · 1 comment

Comments

@saplingt
Copy link
Contributor


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

  • 複習了 getElementById, getElementsByClassName, querySelector, querySelectorAll

    由於js選取器一次只能對一個元素進行操作

    如果要像 jquery 一樣對多個相同class的項目進行操作

    可能要再多跑 while 迴圈
  var n = document.querySelectorAll('.cat').length;
  var i = 0;
  while ( i < n ) {
    document.querySelectorAll('.cat')[i].classList.remove('cute');
  }
  • 複習了忘個精光的 stopPropagation

    心得敘述
  window.addEventListener('click', function(e){
    // bubbling
    e.stopPropagation()

    // 判斷欲排除目標元素
    if ( e.target !== document.querySelector('.cat') ) {
        document.querySelector('.dog').classList.remove('cute');
    }
  })

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

  • 問題一
    還是不太了解 stopPropagation() 的機制

    跟括號內能擺放的東西
  e.stopPropagation()
@shawnlin0201
Copy link
Member

shawnlin0201 commented Nov 22, 2019

@saplingt Hi

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

JavaScript 事件

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

  • addEventListener( )
  • preventDefault( )
  • stopPropagation( )

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 的話:

  • capturing phase 捕獲階段:
    當今天我們點擊了一個 時,點擊事件會從 DOM 的 window 中進入,經過 document、html、body 等等等,到我們的 前,這些過程都算是 capturing phase 捕獲階段

  • target phase 目標階段:
    過了捕獲階段,傳遞到目標元素時,此時就算是 capturing phase 捕獲階段 的終點,同時也是 bubbling phase 冒泡階段 的起點,這個階段統稱 target phase 目標階段

  • bubbling phase 冒泡階段:
    從點擊目標元素開始,到返回 window 的路上,這一階段皆為 bubbling phase 冒泡階段

範例

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

<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 傳遞事件完整的脈絡,若還有問題或不清楚的地方再麻煩你回復囉!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants