DOM Level 2 事件模型


基本事件模型〉的缺點之一,就是只能註冊一個事件處理器,如果你想註冊多個事件處理器,那麼類似以下的方式是行不通的:

window.onload = function() {
    // 處理器一
};

window.onload = function() {
    // 處理器二
};

在上例中,第二個函式實例會成為 onload 參考的對象,而第一個函式實例就不再有用了。

如果想在基本事件處理中,在事件發生時處理時呼叫兩個以上的函式,必須透過設計的方式來達到,最簡單的方式之一就是…

function handler1() {
}

function handler2() {
}

window.onload = function() {
    handler1();
    handler2();
};

但通常這類設計方式不好管理。事件模型在 DOM Level 2 時獲得標準化,又稱為標準事件模型,DOM Level 2 事件模型允許一次註冊兩個以上的事件,在 Internet Explorer 9 之後,DOM Level 2 事件模型也得到比較好的支援了。

在 DOM Level 2 事件模型中,要註冊事件,必須使用 addEventListener 方法。舉個例子來說,若要以 DOM Level 2 事件模型實現〈基本事件模型〉中第一個範例,可以如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
</head>
<body>

    <button id="btn1">按鈕一</button><br>
    <button id="btn2">按鈕二</button><br>
    <div id="console"></div>

<script type="text/javascript">
    function handler() {
        document.getElementById('console').innerHTML 
            = `Who's clicked: ${this.id}`;
    }
    document.getElementById('btn1')
            .addEventListener('click', handler, false);
    document.getElementById('btn2')
            .addEventListener('click', handler, false);
</script>  

</body>
</html>

按我觀看結果

addEventListener 的第一個參數指出要註冊的事件,不需要 'on' 開頭,第二個參數是事件處理器,第三個參數為 false 時,表示這是個事件浮昇處理器(之後文件會再說明)。

在上例中,於事件處理器中使用 this 取得目前觸發事件的元素,雖然目前多數遵守 DOM Level 2 的瀏覽器都會如此實作,不過這並不是 DOM Level 2 標準的規範,在 DOM Level 2 的標準中,可以從 Event 實例的 currentTarget 特性來取得目前觸發事件的元素。

如果你使用 ES6 的箭號函式來做為事件處理器,必須特別注意的是,箭號函式的 this 是根據語彙環境,而不是像 function 根據呼叫者是誰來決定。

在 DOM Level 2 事件模型下,Event 會作為事件處理器的第一個參數傳入,有些事件有預設的動作,若要停止預設動作,在 DOM Level 2 事件模型下,必須呼叫 EventpreventDefault 方法(而不是像基本事件模型那樣,從事件處理器中傳回 false)。例如可改寫〈基本事件模型〉中第三個表單驗證範例:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
</head>
<body>

  <form name="form1" action="fake.do">
      輸入資料:<input name="data"><br>
      <button type="submit">送出</button>
  </form>  

<script type="text/javascript">
    document.form1.addEventListener('submit', event => {
        if(event.currentTarget.data.value.length === 0) {
            event.preventDefault();
        }
    }, false);
</script>  

</body>
</html>

按我觀看結果

若要移除事件處理器,則可以使用 removeEventListener,第一個參數指定事件類型,第二個參數是當初註冊的函式實例,第三個參數指出要移除捕捉階段(true)或浮昇階段(false)的處理器,下一篇文件就會加以說明。