DOM事件處理

DOM(Document Object Model)

  • HTML的程式介面,是網頁與使用者互動的重要部分
  • 提供了文件以擁有屬性與函式的節點與物件組成的結構化表示
  • 每一個節點皆為物件(Object)且擁有各自的屬性以及方法,允許在JavaScript當中操作HTML元素

DOM Tree

事件處理

說明

  • 通常是由使用者的操作行為產生(例如:滑鼠點擊、鍵盤輸入...)
  • 當某個事件在某個元素上發生時,可以撰寫JS做出對應的動作

方法

  • addEventListener : 新增事件監聽
  • removeEventListener : 移除事件監聽
const btn = document.getElementById("myButton");
const myAlert = () => {
    alert('Hello Event!')
}

// 新增事件監聽
btn.addEventListener("click", myAlert)

// 移除事件監聽
btn.removeEventListener('click', myAlert)

事件傳遞機制

事件流

  1. 捕獲階段(Capture Phase):
  • 事件從根節點向目標元素傳播,過程中觸發個別元素的捕獲階段事件監聽
  1. 目標階段(Target Phase)
  • 事件達到目標元素
  1. 冒泡階段(Bubbling Phase)
  • 事件從目標元素回到根節點,過程中觸發個別元素的冒泡階段事件監聽

先捕獲,後冒泡

DOM事件流程

指定階段觸發

  • 可以使用 addEventListener 的第三個參數來指定事件處理函數在哪個階段應該被觸發
  • 使用 true 或 {capture: true}: 將在捕獲階段觸發
  • 使用 false 或 {capture: false}: 將在冒泡階段觸發
  • 不加入參數,預設為 false ,事件處理會在冒泡階段觸發
// 在冒泡階段觸發(預設)
element.addEventListener('click', function() {
  console.log('Bubbling phase');
});

// 在捕獲階段觸發
element.addEventListener('click', function() {
  console.log('Capture phase');
}, true);

事件冒泡問題

  • 當在冒泡階段時,事件從目標元素向上冒泡至根元素,並觸發所經過元素的事件監聽
  • 過程中可能會觸發非目標元素的事件,發生非預期行為

範例

HTML結構

<div id="parent">
  <button id="child">Click!</button>
</div>

JavaScript程式碼

document.getElementById('child').addEventListener('click', function() {
  alert('Child Button Clicked!');
});

document.getElementById('parent').addEventListener('click', function() {
  alert('Parent Div Clicked!');
});

點擊 "Click!" 按鈕時

  1. 先看到 "Child Button Clicked!"
  2. 然後會是 "Parent Div Clicked!" (不需要觸發的事件)

取消事件傳遞

event.stopPropagation()

  • 阻止事件繼續冒泡或捕獲,但不會阻止同一個元素上的其他事件監聽器被觸發
<div id="parent"> <button id="child">Click Me!</button> </div>
//  第二個監聽器(`Second Listener: This will still fire.`)仍然會被觸發

document.getElementById('child').addEventListener('click', function(event) {
  alert('First Listener: Child Button Clicked!');
  event.stopPropagation();
});

document.getElementById('child').addEventListener('click', function() {
  alert('Second Listener: This will still fire.');
});

document.getElementById('parent').addEventListener('click', function() {
  alert('Parent Div Clicked!');  // This will not be triggered
});

event.stopImmediatePropagation()

  • 除了阻止事件繼續冒泡或捕獲,還會阻止同一個元素上的其他事件監聽器被觸發
//只會觸發'First Listener: Child Button Clicked!'

document.getElementById('child').addEventListener('click', function(event) {
  alert('First Listener: Child Button Clicked!');
  event.stopImmediatePropagation();
});

document.getElementById('child').addEventListener('click', function() {
  alert('Second Listener: This will not fire.');
});

document.getElementById('parent').addEventListener('click', function() {
  alert('Parent Div Clicked!');  // This will not be triggered
});

事件委派 ( Event Delegation)

  • 利用事件冒泡的特性,可以只新增事件監聽器到父元素,處理多個子元素的事件
  • 減少監聽器數量, 讓程式碼更有效率

範例

  • 使用 event.target ,可以判斷哪個子元素被點擊
document.getElementById('parent').addEventListener('click', function(event) {
  if(event.target.id === 'child') {
    alert('Child Button Clicked through Delegation!');
  }
});

參考資料

# JavaScript