瀏覽器的事件傳遞機制


Posted by tzutzu858 on 2020-08-06

先來個範例

<head>
  <style>
    .outer {
      width: 500px;
      height: 200px;
      background: red;
    }

    .inner {
      width: 300px;
      height: 100px;
      background: green;
    }
  </style>
</head>

<body>
  <div class="outer">
    <div class="inner">
      <button class="btn">click me</button>
    </div>
  </div>

  <script>
    addEvent(".outer")
    addEvent(".inner")
    addEvent(".btn")

    function addEvent(className) {
      document.querySelector(className)
        .addEventListener('click', function () {
          console.log(className)
        })
    }
  </script>
</body>

當點下最小的 btn ,三個事件都會被觸發,點綠色,綠和紅都會被觸發。


事件傳遞機制詳解:捕獲與冒泡

好文推薦 : DOM 的事件傳遞機制:捕獲與冒泡

w3c 講 event flow 的圖

.addEventListener(1,2,3)

  1. 第一個參數是事件名稱
  2. 第二個參數是 callback function
  3. 第三個參數是 boolean 變數

如果 boolean 變數是 false ,會放在冒泡階段上,如果是 true ,會放在捕獲階段上

事件傳遞順序 :

  1. 先捕獲,再冒泡
  2. 當事件傳到 target 本身,沒有分捕獲跟冒泡

當我們點擊 box 的事件時,click 會先傳給 window,一直往下傳,同一個事件再傳回來


不管有沒有加事件監聽,事件都會是這樣傳
那加一個事件監聽

 document.querySelector('.box')
        .addEventListener('click', function () {
          console.log('box click')
        })

當傳傳傳,往上傳時,listener 就可以聽到那個事件


加兩個事件監聽,當我按下 box 的 click 兩個事件都會監聽到。
log box click 再來 log inner click

document.querySelector('.box')
        .addEventListener('click', function (e) {
          console.log('box click')
        })

document.querySelector('.inner')
        .addEventListener('click', function (e) {
          console.log('inner click')
        })


如果在 box 監聽事件上加 e.stopPropagation()
那就只會 log box click 上面的 log inner click 就不會顯現出來

document.querySelector('.box')
        .addEventListener('click', function (e) {
          e.stopPropagation()
          console.log('box click')
        })

document.querySelector('.inner')
        .addEventListener('click', function (e) {
          console.log('inner click')
        })


改變監聽事件的位置,加入 .addEventListener 的第三個參數,false 會放在冒泡階段上,true 會放在捕獲階段上,下面執行完會先 log inner click ,再 log box click

 document.querySelector('.box')
        .addEventListener('click', function (e) {
          e.stopPropagation()
          console.log('box click')
        }, false)

      document.querySelector('.inner')
        .addEventListener('click', function (e) {
          console.log('inner click')
        }, true)


  • 如果在 windows 上加 e.stopPropagation() 那不管在哪邊加 Listener 都不會被執行到,事件都不會被往下傳。

  • 上面有說當事件傳到 target 本身,沒有分捕獲跟冒泡,所以是按照程式寫的順序。因此下面會先 log box 冒泡 接著才是 box 捕獲

document.querySelector('.box')
  .addEventListener('click', function (e) {
      e.stopPropagation()
      console.log('box 冒泡')
   }, false)

document.querySelector('.box')
  .addEventListener('click', function (e) {
      e.stopPropagation()
      console.log('box 捕獲')
   }, true)

在同一個地方是可以放兩個 Listener

如果其中一個放 e.stopPropagation() 兩個事件都還是被執行,因為他們在同一層,如果只要一個被執行,那就要下 e.stopImmediatePropagation() ,會阻止任何的 EventListener


e.stopPropagatione.preventDefault 的差別
e.stopPropagation : 取消事件繼續往下傳遞
e.preventDefault : 取消瀏覽器的預設行為,和傳遞事件完全無關
取消瀏覽器的預設行為例如阻止超連結,參考這篇
表單提交(submit)時使用preventDefault可能產生的問題

[筆記][JavaScript]所謂的「停止事件」到底是怎麼一回事?










Related Posts

Custom HTML5 Video Player

Custom HTML5 Video Player

基本應用題

基本應用題

The introduction and difference between class component and function component in React

The introduction and difference between class component and function component in React


Comments