瀏覽器的事件傳遞機制


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

CSS保健室|border-image-repeat

CSS保健室|border-image-repeat

[Note] Git: 基本觀念

[Note] Git: 基本觀念

React(5) - controlled & uncontrolled component

React(5) - controlled & uncontrolled component


Comments