Skip to content

DOM 事件級別:DOM0 到 DOM3 與 addEventListener

DOM 事件級別:onclick 與 addEventListener

DOM 事件級別 說的其實是同一件事在不同年代的做法:要把一段程式掛到某個 事件 上,該怎麼寫。了解演進,才知道為什麼現在一律推薦 addEventListener()

DOM Level 0

簡稱 DOM0,在 HTML 標籤內直接寫 JavaScript。

html
<img
  src="./images/logo.jpg"
  width="300"
  height="400"
  alt="Logo"
  onclick="alert('Hello JavaScript!')"
>

透過 HTML 定義了 id 屬性,選取網頁中 id 相符的 DOM,再將這個 DOM 綁定 onclick 事件,onclick 觸發這個事件處理函式。最後,利用事件處理賦值為 null 來解綁事件,也就是 document.getElementById('Pic').onclick = null

html
<body>
  <img src="./images/logo.jpg" id="Pic" width="300" height="400" alt="Logo">
  <script>
    document.getElementById('Pic').onclick = function () {
      alert('Hello JavaScript');
    };
  </script>
</body>

注意

  • HTML 和 JavaScript 的依賴性太強。
  • 容易被駭客植入惡意程式碼。
  • 一個事件只能綁定一個函式。如果用此方法在同一個事件上綁定兩個不同函式,會造成兩個函式都無法如預期般正確運作。

DOM Level 1

簡稱 DOM1,沒有定義事件相關內容,因此沒有 DOM1 事件模型。它處理的是文件結構本身的規範,例如:節點的建立與操作。

DOM Level 2

簡稱 DOM2,DOM2 彌補了 DOM0 無法同時綁定多個處理函式。DOM2 定義了 addEventListenerremoveEventListener 兩個方法,分別用來綁定與解綁事件。

html
<body>
  <img src="./images/logo.jpg" id="Pic" width="300" height="400" alt="Logo">
  <script>
    document.getElementById('Pic').addEventListener('click', function(){
      alert('Hello JavaScript');
    });
  </script>
</body>

方法中可使用三個參數,以下方程式碼為例:

js
element.addEventListener(eventType, eventHandler, useCapture);
參數狀態型別說明
eventType必填string事件類型
eventHandler必填function事件觸發時要執行的函式
useCapture選填boolean指定採用事件冒泡或捕獲。

小提醒

useCapture:true 為捕獲,false 為冒泡,第三個參數沒填時,預設為 false。冒泡與捕獲的差別請看 事件傳播

解綁事件

removeEventListener() 需要傳入 與綁定時同一個函式參考,所以匿名函式是解不掉的:

js
const elemPic = document.querySelector('#Pic');

function showMsg() {
  alert('Hello JavaScript');
}

// 綁定
elemPic.addEventListener('click', showMsg);

// 解綁,成功
elemPic.removeEventListener('click', showMsg);

注意

下面這種寫法看起來一樣,但綁定與移除是兩個不同的函式,因此解不掉:

js
elemPic.addEventListener('click', () => alert('Hi'));
elemPic.removeEventListener('click', () => alert('Hi'));

第三個參數也可以是物件

現代寫法把第三個參數改成物件,能設定更多行為:

js
elemPic.addEventListener('click', showMsg, {
  // 只觸發一次,之後自動移除
  once: true,
  // 在捕獲階段觸發
  capture: false,
  // 承諾不呼叫 preventDefault(),可提升捲動效能
  passive: true,
});

DOM Level 3

簡稱 DOM3,以 DOM2 為基礎,增添了更多的事件類型,例如:滑鼠滾輪、複合輸入(IME)與更完整的鍵盤事件定義。

三種寫法的比較

項目HTML 屬性(DOM0)onclick 賦值(DOM0)addEventListener()(DOM2)
結構與邏輯分離
可綁定多個函式
可指定捕獲階段
建議使用不建議少用推薦

常見問題

為什麼不建議把事件寫在 HTML 的 onclick 屬性?

因為 HTML 與程式邏輯綁得太死、一個事件只能綁一個函式,屬性內的字串也可能成為被注入惡意程式碼的破口。改用 addEventListener() 綁定即可避免,寫法可參考 就來寫個事件處理

addEventListener 的第三個參數是做什麼的?

它是 useCapture,決定這個監聽器在 捕獲階段或冒泡階段 被觸發。true 為捕獲,false 為冒泡,不填時預設為 false

removeEventListener 為什麼移除不掉監聽器?

因為移除時傳入的函式必須與綁定時是同一個函式參考。綁定時若寫的是 匿名函式,就沒有東西可以傳給 removeEventListener(),請先把函式存成具名函式或變數。

同一個元素可以綁定多個相同事件嗎?

addEventListener() 可以,多個處理函式會依綁定順序全部執行。DOM0 的 onclick 寫法則會互相覆蓋,只有最後一個生效。

有沒有只想觸發一次的寫法?

有。第三個參數改成物件並設定 oncetrue,監聽器執行一次後就會自動移除,不必手動呼叫 removeEventListener()

延伸閱讀