Skip to content

Javascript 的事件傳播

事件傳播 = 事件流,就是描述頁面中接受事件的順序。大致上,可以分成兩種機制:

  • 冒泡(Bubbling)。
  • 捕獲(Capture)。

冒泡

代表從觸發事件的元素開始,向外傳遞,直到網頁的根節點為止。如下方程式碼所式,我們點擊了 <h1> 元素,事件將會層層向外傳遞。

其順序為:<h1> -> <body> -> <html> -> document -> window

html
<!DOCTYPE html>
<html>
  <head>
    <title>Javascript - 事件傳遞</title>
  </head>
  <body>
    <h1>click</h1>
  </body>
</html>

測試以下程式碼,從最內層向外傳遞。以 console.log() 印出來的內容,

依序為:in -> middle -> out -> body -> html -> document -> window。

html
<!DOCTYPE html>
<html>
  <head>
    <title>Javascript - 事件傳遞</title>
  </head>
  <body>
    <div class="box-out" id="BoxOut">
      <div class="box-middle" id="BoxMiddle">
        <div class="box-in" id="BoxIn"></div>
      </div>
    </div>
  </body>
</html>
js
window.addEventListener('click', () => {
  console.log('window');
}, false)

document.addEventListener('click', () => {
  console.log('document');
}, false);

document.querySelector('html').addEventListener('click', () => {
  console.log('html');
}, false);

document.body.addEventListener('click', () => {
  console.log('body');
}, false);

document.querySelector('#BoxOut').addEventListener('click', () => {
  console.log('out');
}, false);

document.querySelector('#BoxMiddle').addEventListener('click', () => {
  console.log('middle');
}, false);

document.querySelector('#BoxIn').addEventListener('click', () => {
  console.log('in');
}, false);
css
body, html {
  height: 100%;
}

.box-out {
  width: 500px;
}

.box-out,
.box-middle,
.box-in {
  padding: 30px;
}

.box-out {
  background-color: yellowgreen;
}

.box-middle {
  background-color: cadetblue;
}

.box-in {
  background-color: pink;
}

TIP

監聽器的第三個參數為選填,在沒有設定的狀況下,預設為 false。因此,若沒有設定或設定為 false 都是向外傳遞的冒泡機制,

捕獲

此傳遞的方式剛好與冒泡相反,也就是由外向內傳遞。以下方的程式碼為例:

其順序為:window -> document -> <html> -> <body> -> <h1>

html
<!DOCTYPE html>
<html>
  <head>
    <title>Javascript - 事件傳遞</title>
  </head>
  <body>
    <h1>click</h1>
  </body>
</html>

測試以下程式碼,從最外層向內傳遞。以 console.log() 印出來的內容,

依序為:window -> document -> html -> body -> out -> middle -> in。

html
<!DOCTYPE html>
<html>
  <head>
    <title>Javascript - 事件傳遞</title>
  </head>
  <body>
    <div class="box-out" id="BoxOut">
      <div class="box-middle" id="BoxMiddle">
        <div class="box-in" id="BoxIn"></div>
      </div>
    </div>
  </body>
</html>
js
window.addEventListener('click', () => {
  console.log('window');
}, true)

document.addEventListener('click', () => {
  console.log('document');
}, true);

document.querySelector('html').addEventListener('click', () => {
  console.log('html');
}, true);

document.body.addEventListener('click', () => {
  console.log('body');
}, true);

document.querySelector('#BoxOut').addEventListener('click', () => {
  console.log('out');
}, true);

document.querySelector('#BoxMiddle').addEventListener('click', () => {
  console.log('middle');
}, true);

document.querySelector('#BoxIn').addEventListener('click', () => {
  console.log('in');
}, true);
css
body, html {
  height: 100%;
}

.box-out {
  width: 500px;
}

.box-out,
.box-middle,
.box-in {
  padding: 30px;
}

.box-out {
  background-color: yellowgreen;
}

.box-middle {
  background-color: cadetblue;
}

.box-in {
  background-color: pink;
}

阻擋事件傳播 event.stopPropagation()

如果要阻擋事件外擴張,就可以利用到事件物件中的方法: event.stopPropagation()

html
<!DOCTYPE html>
<html>

  <head>
    <title>Javascript - 事件傳遞</title>
  </head>

  <body>
    <div class="box-out" id="BoxOut">
      <div class="box-in" id="BoxIn"></div>
    </div>
  </body>

</html>
js
document.querySelector('#BoxOut').addEventListener('click', () => {
  console.log('out');
});

document.querySelector('#BoxIn').addEventListener('click', (e) => {
  e.stopPropagation();
  console.log('in');
});
css
.box-out,
.box-in {
  padding: 30px;
}

.box-out {
  background-color: yellowgreen;
}

.box-in {
  background-color: pink;
}

取消預設行為 event.preventDefault()

在 HTML 的元素中,有某些元素都有預設行為。像是 <a> 會有連結的動作、<input type="submit"> 會有送出表單的動作。如果我們需要綁定某些事件在元素上,但元素上卻有一些預設的 HTML 行為,將會影響我們安排好的工作。這樣一來,取消預設行為就是一件很重要的事情。

html
<a id="Link" href="http://tw.yahoo.com">前往 Yahoo!</a>
js
document.querySelector('#Link').addEventListener('click', (e) => {
  e.preventDefault();
});