DOM 事件級別:DOM0 到 DOM3 與 addEventListener
DOM 事件級別 說的其實是同一件事在不同年代的做法:要把一段程式掛到某個 事件 上,該怎麼寫。了解演進,才知道為什麼現在一律推薦 addEventListener()。
DOM Level 0
簡稱 DOM0,在 HTML 標籤內直接寫 JavaScript。
<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。
<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 定義了 addEventListener 和 removeEventListener 兩個方法,分別用來綁定與解綁事件。
<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>方法中可使用三個參數,以下方程式碼為例:
element.addEventListener(eventType, eventHandler, useCapture);| 參數 | 狀態 | 型別 | 說明 |
|---|---|---|---|
| eventType | 必填 | string | 事件類型 |
| eventHandler | 必填 | function | 事件觸發時要執行的函式 |
| useCapture | 選填 | boolean | 指定採用事件冒泡或捕獲。 |
小提醒
useCapture:true 為捕獲,false 為冒泡,第三個參數沒填時,預設為 false。冒泡與捕獲的差別請看 事件傳播。
解綁事件
removeEventListener() 需要傳入 與綁定時同一個函式參考,所以匿名函式是解不掉的:
const elemPic = document.querySelector('#Pic');
function showMsg() {
alert('Hello JavaScript');
}
// 綁定
elemPic.addEventListener('click', showMsg);
// 解綁,成功
elemPic.removeEventListener('click', showMsg);注意
下面這種寫法看起來一樣,但綁定與移除是兩個不同的函式,因此解不掉:
elemPic.addEventListener('click', () => alert('Hi'));
elemPic.removeEventListener('click', () => alert('Hi'));第三個參數也可以是物件
現代寫法把第三個參數改成物件,能設定更多行為:
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 寫法則會互相覆蓋,只有最後一個生效。
有沒有只想觸發一次的寫法?
有。第三個參數改成物件並設定 once 為 true,監聽器執行一次後就會自動移除,不必手動呼叫 removeEventListener()。