DOM事件的工作原理

導(dǎo)讀:
本文是teren對(duì)DOM事件知識(shí)點(diǎn)所做的進(jìn)一步整理,整理資料主要參考DOM事件簡(jiǎn)介饑人谷課件,如果對(duì)DOM事件有什么不了解的地方,可以直接參考原文出處。
這篇筆記的目的有兩個(gè),一是作為自己整理的資料方便日后查閱,畢竟自己整理的資料用起來更加得心應(yīng)手,思路更加契合自己;二是希望通過在撰寫筆記過程中強(qiáng)化記憶;
如果這篇文章有什么能夠幫助到各位讀者,我要萬分感謝原文的作者以及原文的翻譯?。?!
最后,這篇文章的整理結(jié)構(gòu)如目錄所示。

目錄
1.預(yù)備知識(shí)
監(jiān)聽事件
移除事件
2.事件階段
3.event對(duì)象
4.事件的操作
停止事件傳播
阻止瀏覽器的默認(rèn)行為
自定義事件
代理事件監(jiān)聽
5.FQA
DOM 0事件和DOM 2事件
事件流的三種模型
IE兼容性


1. 預(yù)備知識(shí)

監(jiān)聽事件

在為節(jié)點(diǎn)添加事件時(shí),推薦使用addEventListener接口(IE的使用attachEvent接口)

node.addEventListener(eventname,callback[,useCapture])
  • eventname:監(jiān)聽事件的名稱,如
    click/mouseover/mouseout/mousedown/mouseup/load/unload等事件

  • callback:事件觸發(fā)時(shí)被調(diào)用的函數(shù),此時(shí)會(huì)自動(dòng)產(chǎn)生一個(gè)event對(duì)象,作為第一個(gè)參數(shù)傳入callback

var ul = document.querySelector('ul')
ul.addEventListener('click',function(event){console.log(event.target)})

關(guān)于event對(duì)象后面詳細(xì)講解

  • useCapture:決定回調(diào)函數(shù)(callback)是否在“捕獲(capture)”階段被觸發(fā),,默認(rèn)是false,即在冒泡階段被觸發(fā)
    關(guān)于事件階段后面詳解,現(xiàn)在給個(gè)范例演示,可以看完事件階段章節(jié)后再“回調(diào)”查看(~ ̄▽ ̄)~
    demo: usecapture
移除事件

移除事件使用removeEventListener接口

node.removeEventListener(eventname,callback)

值的注意的是:
通過addEventListener添加的事件處理程序只能通過removeEventListener移除,移除時(shí)參數(shù)與添加的時(shí)候相同;
這也就意味著:在移除事件時(shí)回調(diào)函數(shù)不能為匿名函數(shù),因?yàn)槟涿瘮?shù)雖然方法體一樣,但是句柄(可以理解為函數(shù)名)卻不相同
也就是說當(dāng)初定義回調(diào)函數(shù)必須以以下形成出現(xiàn)

var ul  = document.querySelector('ul')
//method 1
function printHello(){
  alert('hello world')
}
//method 2
handler = function (){
  alert('hello world 2')
}
ul.addEventListener('click',printHello);
ul.addEventListener('click',handler);
ul.addEventListener('click',function(){alert('hello world 3')})
ul.removeEventListener('click',printHello);
//此時(shí)你要移除第3個(gè)hello world,那么你無法碼下去
//ul.removeEventListener('click',)

demo : removeEvent

2.事件階段

以一個(gè)例子去描述事件階段

  <ul>
    <li id="demo1">demo1</li>
    <li id="demo2">demo2</li>
    <li id="demo3">demo3</li>
  </ul>
var demo2 = document.querySelector('#demo2')
demo2.addEventListener('click',callback)
function callback(){
  console.log('demo')
}

給li#demo2節(jié)點(diǎn)添加click事件,在點(diǎn)擊li#demo2節(jié)點(diǎn)時(shí),點(diǎn)擊事件不是直接在該節(jié)點(diǎn)直接發(fā)生,而是分為三個(gè)事件階段:

  • click事件從html文檔的根節(jié)點(diǎn)window流向目標(biāo)節(jié)點(diǎn)li#demo2(捕獲階段)
  • 然后在目標(biāo)節(jié)點(diǎn)上click事件觸發(fā)(目標(biāo)階段)
  • 最后再返回到文檔的根節(jié)點(diǎn)(冒泡階段)


    事件階段

demo:event phases
小結(jié):
事件觸發(fā)的整個(gè)過程可分為三個(gè)階段:

  • 捕獲階段
    事件的第一個(gè)階段是捕獲階段。事件從文檔的根節(jié)點(diǎn)出發(fā),隨著DOM樹的結(jié)構(gòu)向事件的目標(biāo)節(jié)點(diǎn)流去。途中經(jīng)過各個(gè)層次的DOM節(jié)點(diǎn),并在各節(jié)點(diǎn)上觸發(fā)捕獲事件,直到到達(dá)事件的目標(biāo)節(jié)點(diǎn)。

類似水流一樣,從源頭流向目的地

  • 目標(biāo)階段
    當(dāng)事件到達(dá)目標(biāo)節(jié)點(diǎn)的,事件就進(jìn)入了目標(biāo)階段。事件在目標(biāo)節(jié)點(diǎn)上被觸發(fā),然后會(huì)逆向回流,直到傳播至最外層的文檔節(jié)點(diǎn)
    【注】
    或許有人會(huì)疑問?事件在目標(biāo)節(jié)點(diǎn)被觸發(fā),那么設(shè)置usecapture還有什么用處呢?
    我的理解是:
    你為節(jié)點(diǎn)設(shè)置事件是一回事,你觸發(fā)事件時(shí)是另一回事;
    節(jié)點(diǎn)的事件觸發(fā)時(shí)點(diǎn)可分為上述三個(gè)階段,在乎你怎么設(shè)置
    下面代碼中,li#demo2一定是在目標(biāo)階段被觸發(fā),而ul則在乎你的設(shè)置,下例設(shè)置為捕獲階段被觸發(fā)
var ul = document.querySelector('ul')
var demo2  = document.querySelector('#demo2')
function printUl(){alert('Ul')}
function printList(){
  alert('List')
}
//當(dāng)點(diǎn)擊li#demo2時(shí),到了目標(biāo)階段觸發(fā)li#demo2的click事件
//當(dāng)你為ul的usecapture設(shè)置true時(shí),意味著ul在捕獲階段觸發(fā)click事件
ul.addEventListener('click',printUl,true)
demo2.addEventListener('click',printList)

demo:how to recognize event phases

  • 冒泡階段
    事件在目標(biāo)元素上觸發(fā)后,并不在這個(gè)元素上終止。它會(huì)隨著DOM樹一層層向上冒泡,直到到達(dá)最外層的根節(jié)點(diǎn)

3.event對(duì)象

event對(duì)象是在事件第一次觸發(fā)時(shí)候被創(chuàng)建,并且一直伴隨著事件在DOM結(jié)構(gòu)中流轉(zhuǎn)的整個(gè)生命周期。
event對(duì)象會(huì)被作為第一個(gè)參數(shù)傳遞給事件監(jiān)聽的回調(diào)函數(shù)。
event對(duì)象中包含大量當(dāng)前事件相關(guān)的信息:

屬性/方法 備注
type 事件名稱
target 事件的目標(biāo)節(jié)點(diǎn)
currentTarget 事件觸發(fā)時(shí)的當(dāng)前節(jié)點(diǎn)
bubbles 判斷節(jié)點(diǎn)的事件是否是在冒泡階段捕獲
preventDefault(function) 阻止瀏覽器中用戶代理對(duì)當(dāng)前事件的相關(guān)默認(rèn)行為被觸發(fā)。比如阻止a元素的click事件加載一個(gè)新的頁面
cancelable(boolean) 指明這個(gè)事件的默認(rèn)行為是否可以通過調(diào)用event.preventDefault來阻止,即只有cancelable為true的時(shí)候,調(diào)用event.preventDefault才能生效
stopPropagation(function) 阻止當(dāng)前事件流上后面的元素的回調(diào)函數(shù)被觸發(fā),當(dāng)前節(jié)點(diǎn)上針對(duì)此事件的其他回調(diào)函數(shù)依然會(huì)被觸發(fā)
stopImmediatePropagation(function) 阻止當(dāng)前事件流上后面所有的回調(diào)函數(shù)被觸發(fā),也包括當(dāng)前節(jié)點(diǎn)上針對(duì)此事件已綁定的其他回調(diào)函數(shù)
eventPhase(number) 表示當(dāng)前這個(gè)事件所處的階段(phase):none(0), capture(1),target(2),bubbling(3)
timeStamp(number) 事件發(fā)生的時(shí)間

下面將一些簡(jiǎn)單的屬性放在下面的示例中,復(fù)雜的方法將在事件操作章節(jié)單獨(dú)羅列
demo : event simple property

4.事件的操作

停止事件傳播

通過調(diào)用事件對(duì)象的stopPropagation方法,在任何階段(捕獲階段或者冒泡階段)中斷事件的傳播;
此后,事件不會(huì)在后面?zhèn)鞑ミ^程中的經(jīng)過的節(jié)點(diǎn)上調(diào)用任何的監(jiān)聽函數(shù);
demo:stopPropagation
但event.stopPropagation()不會(huì)阻止當(dāng)前節(jié)點(diǎn)上此事件其他的監(jiān)聽函數(shù)被調(diào)用。如果你希望阻止當(dāng)前節(jié)點(diǎn)上的其他回調(diào)函數(shù)被調(diào)用的話,你可以使用更激進(jìn)的event.stopImmediatePropagation()
方法;
demo:stopImmediatePropagation

阻止瀏覽器的默認(rèn)行為

當(dāng)特定事件發(fā)生的時(shí)候,瀏覽器會(huì)有一些默認(rèn)的行為作為反應(yīng)。例如,使用a元素時(shí)會(huì)自動(dòng)添加click事件,當(dāng)a元素上click事件觸發(fā)時(shí),它會(huì)向上冒泡直到DOM結(jié)構(gòu)的最外層document,瀏覽器會(huì)解釋href屬性,并且在窗口中加載新地址的內(nèi)容。
如果我們需要阻止瀏覽器針對(duì)點(diǎn)擊事件的默認(rèn)行為,可以調(diào)用event.preventDefault()
demo:preventDefault

自定義事件

【注】
知道有這么一回事,這篇不詳講。

代理事件監(jiān)聽

所謂代理事件監(jiān)聽,指的是不直接在監(jiān)聽的目標(biāo)節(jié)點(diǎn)上添加事件監(jiān)聽函數(shù),而是通過其他的節(jié)點(diǎn)代為監(jiān)聽目標(biāo)節(jié)點(diǎn)的事件;

舉個(gè)例子:
如果有一個(gè)列表ul包含了100個(gè)子元素li,它們都需要對(duì)click事件做出相似的響應(yīng),那么我們可能需要查詢這100個(gè)子元素,并分別為他們添加上事件監(jiān)聽器。這樣的話,我們就會(huì)產(chǎn)生100個(gè)獨(dú)立的事件監(jiān)聽器

代理事件監(jiān)聽可以讓我們更簡(jiǎn)單的處理這種情況。我們不去監(jiān)聽所有的子元素的click事件,相反,我們監(jiān)聽他們的父元素ul。當(dāng)一個(gè)li元素被點(diǎn)擊的時(shí)候,這個(gè)事件會(huì)向上冒泡至ul,觸發(fā)回調(diào)函數(shù)。我們可以通過檢查事件的event.target屬性來判斷具體是哪一個(gè)li被點(diǎn)擊了。
這樣一來,僅僅使用了一個(gè)上層的事件監(jiān)聽器,并且我們不需要在為添加元素而考慮它的事件監(jiān)聽問題
demo:事件代理
但是在實(shí)際代理事件監(jiān)聽中,我們往往使用jQuery提供的on()方法去實(shí)現(xiàn)事件代理
demo:事件代理-on()方法

5.FQA

DOM 0級(jí)事件處理程序和DOM 2級(jí)事件處理程序

首先,了解一下DOM的分級(jí)。
DOM是HTML與XML的應(yīng)用編程接口(API),DOM將整個(gè)頁面映射為一個(gè)由層次節(jié)點(diǎn)組成的文件,有1級(jí)、2級(jí)、3級(jí)共3個(gè)級(jí)別。
1級(jí)DOM
1級(jí)DOM,由DOM核心與DOM HTML兩個(gè)模塊組成。
DOM核心能映射以XML為基礎(chǔ)的文檔結(jié)構(gòu),允許獲取和操作文檔的任意部分。
DOM HTML通過添加HTML專用的對(duì)象與函數(shù)對(duì)DOM核心進(jìn)行了擴(kuò)展。
2級(jí)DOM
鑒于1級(jí)DOM僅以映射文檔結(jié)構(gòu)為目標(biāo),DOM 2級(jí)面向更為寬廣。通過對(duì)原有DOM的擴(kuò)展,2級(jí)DOM通過對(duì)象接口增加了:
DOM視圖:描述跟蹤一個(gè)文檔的各種視圖(使用CSS樣式設(shè)計(jì)文檔前后)的接口;
DOM事件:描述事件接口;
DOM樣式:描述處理基于CSS樣式的接口;
DOM遍歷與范圍:描述遍歷和操作文檔樹的接口;
3級(jí)DOM
3級(jí)DOM通過引入統(tǒng)一方式載入和保存文檔和文檔驗(yàn)證方法對(duì)DOM進(jìn)行進(jìn)一步擴(kuò)展
"0級(jí)"DOM
需要注意的是并沒有標(biāo)準(zhǔn)被稱為0級(jí)DOM,它僅是DOM歷史上一個(gè)參考點(diǎn)(0級(jí)DOM被認(rèn)為是在Internet Explorer 4.0 與Netscape Navigator4.0支持的最早的DHTML)

也就是說:
DOM 0級(jí)事件處理程序是 通過javascript制定事件處理程序的傳統(tǒng)方式,具體實(shí)現(xiàn)方式是:

var btn = document.getElementById("btn");            
btn.onclick = function(){                
alert(this.id);//this指定當(dāng)前元素btn  
}
刪除DOM0事件處理程序,
只要將對(duì)應(yīng)事件屬性置為null即可。btn.onclick = null;

DOM 0級(jí)事件處理程序的優(yōu)點(diǎn)是簡(jiǎn)單且具有跨瀏覽器的優(yōu)勢(shì),缺點(diǎn)是一個(gè)事件處理程序只能對(duì)應(yīng)一個(gè)處理函數(shù)

DOM2級(jí)事件處理程序是在2級(jí)DOM中規(guī)定的API,通過addEventListener(IE為attachEvent)去監(jiān)聽事件,具體實(shí)現(xiàn)方式是:

var btn = document.getElementById("btn");
function handler(){
  alert(this.id)//this指定當(dāng)前元素btn
}
btn.addEventListener('click',handler)

demo:addEventListener
同時(shí)制定了刪除事件處理程序的方法
removeEventListener(IE為detachEvent),關(guān)于removeEventListener的注意事項(xiàng)請(qǐng)?jiān)斠娚衔囊瞥录鹿?jié);
至于attachEvent與addEventListener的區(qū)別詳見后文IE兼容性
addEventListener的優(yōu)點(diǎn)是一個(gè)事件處理程序能對(duì)應(yīng)多個(gè)處理函數(shù),缺點(diǎn)是存在兼容性問題。

事件流的三種模型

所謂事件流,指的是頁面捕獲事件的順序,目前有三種模型:

  • IE的事件冒泡:當(dāng)發(fā)生事件時(shí),目標(biāo)節(jié)點(diǎn)先捕獲,然后逐級(jí)向上傳播到父節(jié)點(diǎn),即事件監(jiān)聽處于冒泡階段

  • Netscape的事件捕獲:當(dāng)發(fā)生事件時(shí),最先觸發(fā)父節(jié)點(diǎn)的事件監(jiān)聽函數(shù),然后逐漸向下傳播到目標(biāo)節(jié)點(diǎn),即事件監(jiān)聽處于捕獲階段

  • 2級(jí)DOM規(guī)定事件流包括三個(gè)階段,事件捕獲階段,處于目標(biāo)階段,事件冒泡階段

IE兼容性

IE并不支持addEventListener和removeEventListener方法,而是實(shí)現(xiàn)了兩個(gè)類似的方法:
attachEvent(eventname,callback)
detachEvent(eventname,callback)
由于IE指支持事件冒泡,所以添加的程序會(huì)被添加到冒泡階段。
【注意】
IE的事件監(jiān)聽的方法與addEventListener方法不同之處包括:
eventname必須包含on以及沒有usecapture;
同時(shí),使用attachEvent方法和addEventListener主要區(qū)別在于事件處理程序的作用域。采用addEventListener,事件處理程序會(huì)在其所屬元素的作用域內(nèi)運(yùn)行。使用attachEvent,事件處理程序會(huì)在全局作用域內(nèi)運(yùn)行,因此this等于window。

var btn = document.getElementById("btn");
function handler(){
  alert(this.id)//this指定window
}
btn.attachEvent('onclick',handler)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 事件是一種異步編程的實(shí)現(xiàn)方式,本質(zhì)上是程序各個(gè)組成部分之間的通信。DOM支持大量的事件,本節(jié)介紹DOM的事件編程。...
    周花花啊閱讀 674評(píng)論 0 3
  • 聲明:本文來源于http://www.webzsky.com/?p=731我只是在這里作為自己的學(xué)習(xí)筆記整理一下(...
    angryyan閱讀 7,239評(píng)論 1 6
  • 以下文章為轉(zhuǎn)載,對(duì)理解JavaScript中的事件處理機(jī)制很有幫助,淺顯易懂,特分享于此。 什么是事件? 事件(E...
    jxyjxy閱讀 3,162評(píng)論 1 10
  • 事件綁定的方式 給 DOM 元素綁定事件分為兩大類:在 html 中直接綁定 和 在 JavaScript 中綁定...
    Bruce_zhuan閱讀 1,128評(píng)論 0 6
  • 可能自己太愛自己女朋友琪寶寶了,每天不敢接近她,因?yàn)槲抑雷约翰粔騼?yōu)秀,我很自卑自己不能讓她炫耀自己男朋友多么厲害...
    維小凱閱讀 319評(píng)論 0 0

友情鏈接更多精彩內(nèi)容