最全的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 定義的事件處理程式是默認在冒泡階段執行。
但是無論事件在哪個階段執行,一個完整的事件流都是先「捕獲階段」,再「冒泡階段」,捕獲和冒泡都檢查一遍。除此之外,還添加了一個「目標階段」,就是用戶真正點擊的元素的事件處理階段。
捕獲階段 ====> 目標階段 ====> 冒泡階段