最全的DOM事件筆記

1. DOM事件模型

DOM是微軟和網景發生「瀏覽器大戰」時期留下的產物,後來被「W3C」進行標準化,標準化一代代升級與改進,目前已經推行至第四代,即 level1(DOM1)、level2(DOM2)、level3(DOM3)、level4(DOM4)。事件模型是DOM的一部分,在不同的發展時期有不同的定義。

1.1 DOM0 / DOM1時期的事件模型

DOM0指的是未被「W3C」標準化前的DOM,DOM被「W3C」正式標準化後才開始的DOM1,因此DOM1是DOM0的整理和歸納,也就是說DOM事件模型最開始的定義是在DOM0 / DOM1 時期,這時的DOM事件模型如下:

① HTML中的onevent事件屬性

//在元素上使用 HTML  attribute on{eventtype}

<div onclick="alert('old')">點擊div</div>

//賦值為事件處理函數
<div onclick="fn()"></div> //必須是加括弧的調用形式
<div onclick="fn.call()"></div> //相當於fn()
<script>
  function fn(){
    console.log('ok');
  }
</script>

//此種方式的事件資訊怎麼傳遞
<div onclick="pe(event)">點擊</div> //這裡必須傳遞 event 關鍵字對象

<script>
  function pe(e){
    console.log(e); //事件資訊
  }
</script>

② js 操作DOM節點的事件屬性

//通過 JavaScript 設置頁面元素相應的事件屬性

let div = document.getElementById("a");
div.onclick = function() { alert('new') };

//當函數單獨定義時
function fn(){
   alert("new");
}
div.onclick = fn;  //必須是不加括弧,傳遞地址的形式

1.2 DOM2時期的事件模型

DOM2中的事件模型已經非常完善了,在DOM3中並未對事件模型進行修改,因此至今的事件模型是以DOM2事件標準為基準的。DOM2標準事件模型採用了事件監聽隊列,如下:

//addEventListener是添加事件監聽,removeEventListener是移除事件監聽

let div = document.getElementById("a");
div.addEventListener('click', function(e){
  console.log('點擊div')
})

//基本語法
target.addEventListener(type, listener, useCapture);
target.addEventListener(type, listener, options);

type:表示監聽事件類型的字元串

listener:事件處理函數,形參為事件資訊

useCapture:Boolean類型的值,表示是否在捕獲階段觸發事件,默認為false,默認在冒泡階段處理事件,若設置為true,則會在捕獲階段處理事件。

options:一個指定有關  listener  屬性的可選參數對象,相比於useCapture,可以有更多的設置,options對象的屬性值都是Boolean值,默認全部為false,
{capture: 是否捕獲階段監聽, once: 是否只監聽一次, passive: 是否忽略preventDefault }

2. DOM事件機制(事件流)

DOM事件流的出現是在DOM節點中事件發生時常見的一種現象中產生的,如下問題:

<div class="爺爺" onclick="console.log('我是爺爺')">
      <div class="爸爸" onclick="console.log('我是爸爸')">
            <div class="兒子" onclick="console.log('我是兒子')">
                  文字
            </div>
      </div>
</div>

//1. 點擊了「文字」後,算不算點擊了兒子?算不算點擊了「爸爸」,算不算點擊了「爺爺」?
//   答案是都算,點擊元素內部的任一元素節點,都算點擊了該元素。這就涉及到一個事件流的問題

由上述可知,事件是會傳遞的,但是事件會以怎樣的順序進行傳遞執行?

在上述的例子中,我們第一眼就想到的就是,點擊了「文字」,會依次向上傳遞,先執行離「文字」最近的兒子的click事件處理程式,再執行父親的,最後執行爺爺的。沒錯,在「瀏覽器大戰」時微軟的IE瀏覽器就是按照由內向外的事件流順序定義DOM事件流的。但是和其對立的網景公司卻是反著定義的,網景公司的DOM事件流傳遞順序是由外層向內層執行,先執行爺爺的,再執行爸爸的,最後執行兒子的。

在W3C規定的DOM2中統一了DOM事件機制的標準,即規定事件的傳遞順序是先從外層向內層依次傳遞,稱之為「捕獲階段」,再從內層向外層依次傳遞,稱之為「冒泡階段」。但並不意味著一個事件處理程式要在「捕獲」和「冒泡」同時執行兩次,而是用戶選擇其事件處理程式的執行時期是在「捕獲階段」還是「冒泡階段」。在DOM2的 addEventListener函數的第三個參數(useCapture)就是讓用戶選擇該處理程式是放在「捕獲階段」執行還是「冒泡階段」執行。默認是 false,即事件監聽機制默認是在「冒泡階段」,也就是用 addEventListener 定義的事件處理程式是默認在冒泡階段執行。

但是無論事件在哪個階段執行,一個完整的事件流都是先「捕獲階段」,再「冒泡階段」,捕獲和冒泡都檢查一遍。除此之外,還添加了一個「目標階段」,就是用戶真正點擊的元素的事件處理階段。

捕獲階段 ====> 目標階段 ====> 冒泡階段

3. DOM事件委託

4. DOM事件對象

4.1 target VS currentTarget

5. 自定義事件