導(dǎo)讀:
本文是teren對(duì)DOM事件知識(shí)點(diǎn)所做的進(jìn)一步整理,整理資料主要參考DOM事件簡(jiǎn)介和饑人谷課件蟆肆,如果對(duì)DOM事件有什么不了解的地方杀糯,可以直接參考原文出處。
這篇筆記的目的有兩個(gè)路鹰,一是作為自己整理的資料方便日后查閱,畢竟自己整理的資料用起來(lái)更加得心應(yīng)手收厨,思路更加契合自己晋柱;二是希望通過(guò)在撰寫(xiě)筆記過(guò)程中強(qiáng)化記憶;
如果這篇文章有什么能夠幫助到各位讀者诵叁,我要萬(wàn)分感謝原文的作者以及原文的翻譯Q憔骸!拧额!
最后碑诉,這篇文章的整理結(jié)構(gòu)如目錄所示。
目錄
1.預(yù)備知識(shí)
監(jiān)聽(tīng)事件
移除事件
2.事件階段
3.event對(duì)象
4.事件的操作
停止事件傳播
阻止瀏覽器的默認(rèn)行為
自定義事件
代理事件監(jiān)聽(tīng)
5.FQA
DOM 0事件和DOM 2事件
事件流的三種模型
IE兼容性
1. 預(yù)備知識(shí)
監(jiān)聽(tīng)事件
在為節(jié)點(diǎn)添加事件時(shí)侥锦,推薦使用addEventListener接口(IE的使用attachEvent接口)
node.addEventListener(eventname,callback[,useCapture])
eventname:監(jiān)聽(tīng)事件的名稱进栽,如
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)
值的注意的是:
通過(guò)addEventListener添加的事件處理程序只能通過(guò)removeEventListener移除唠帝,移除時(shí)參數(shù)與添加的時(shí)候相同;
這也就意味著:在移除事件時(shí)回調(diào)函數(shù)不能為匿名函數(shù)玄柏,因?yàn)槟涿瘮?shù)雖然方法體一樣襟衰,但是句柄(可以理解為函數(shù)名)卻不相同
也就是說(shuō)當(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,那么你無(wú)法碼下去
//ul.removeEventListener('click',)
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è)過(guò)程可分為三個(gè)階段:
- 捕獲階段
事件的第一個(gè)階段是捕獲階段赶熟。事件從文檔的根節(jié)點(diǎn)出發(fā)瑰妄,隨著DOM樹(shù)的結(jié)構(gòu)向事件的目標(biāo)節(jié)點(diǎn)流去。途中經(jīng)過(guò)各個(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ì)疑問(wèn)秒拔?事件在目標(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樹(shù)一層層向上冒泡,直到到達(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)聽(tīng)的回調(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è)新的頁(yè)面 |
cancelable(boolean) | 指明這個(gè)事件的默認(rèn)行為是否可以通過(guò)調(diào)用event.preventDefault來(lái)阻止,即只有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.事件的操作
停止事件傳播
通過(guò)調(diào)用事件對(duì)象的stopPropagation方法废离,在任何階段(捕獲階段或者冒泡階段)中斷事件的傳播侄泽;
此后,事件不會(huì)在后面?zhèn)鞑ミ^(guò)程中的經(jīng)過(guò)的節(jié)點(diǎn)上調(diào)用任何的監(jiān)聽(tīng)函數(shù)蜻韭;
demo:stopPropagation
但event.stopPropagation()不會(huì)阻止當(dāng)前節(jié)點(diǎn)上此事件其他的監(jiān)聽(tīng)函數(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)聽(tīng)
所謂代理事件監(jiān)聽(tīng),指的是不直接在監(jiān)聽(tīng)的目標(biāo)節(jié)點(diǎn)上添加事件監(jiān)聽(tīng)函數(shù)截亦,而是通過(guò)其他的節(jié)點(diǎn)代為監(jiān)聽(tīng)目標(biāo)節(jié)點(diǎn)的事件爬泥;
舉個(gè)例子:
如果有一個(gè)列表ul包含了100個(gè)子元素li,它們都需要對(duì)click事件做出相似的響應(yīng)魁巩,那么我們可能需要查詢這100個(gè)子元素急灭,并分別為他們添加上事件監(jiān)聽(tīng)器。這樣的話谷遂,我們就會(huì)產(chǎn)生100個(gè)獨(dú)立的事件監(jiān)聽(tīng)器
代理事件監(jiān)聽(tīng)可以讓我們更簡(jiǎn)單的處理這種情況葬馋。我們不去監(jiān)聽(tīng)所有的子元素的click事件,相反肾扰,我們監(jiān)聽(tīng)他們的父元素ul畴嘶。當(dāng)一個(gè)li元素被點(diǎn)擊的時(shí)候,這個(gè)事件會(huì)向上冒泡至ul集晚,觸發(fā)回調(diào)函數(shù)窗悯。我們可以通過(guò)檢查事件的event.target屬性來(lái)判斷具體是哪一個(gè)li被點(diǎn)擊了。
這樣一來(lái)偷拔,僅僅使用了一個(gè)上層的事件監(jiān)聽(tīng)器蒋院,并且我們不需要在為添加元素而考慮它的事件監(jiān)聽(tīng)問(wèn)題
demo:事件代理
但是在實(shí)際代理事件監(jiān)聽(tīng)中,我們往往使用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è)頁(yè)面映射為一個(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通過(guò)添加HTML專用的對(duì)象與函數(shù)對(duì)DOM核心進(jìn)行了擴(kuò)展。
2級(jí)DOM
鑒于1級(jí)DOM僅以映射文檔結(jié)構(gòu)為目標(biāo)沦偎,DOM 2級(jí)面向更為寬廣马昨。通過(guò)對(duì)原有DOM的擴(kuò)展竞帽,2級(jí)DOM通過(guò)對(duì)象接口增加了:
DOM視圖:描述跟蹤一個(gè)文檔的各種視圖(使用CSS樣式設(shè)計(jì)文檔前后)的接口;
DOM事件:描述事件接口鸿捧;
DOM樣式:描述處理基于CSS樣式的接口屹篓;
DOM遍歷與范圍:描述遍歷和操作文檔樹(shù)的接口;
3級(jí)DOM
3級(jí)DOM通過(guò)引入統(tǒng)一方式載入和保存文檔和文檔驗(yàn)證方法對(duì)DOM進(jìn)行進(jìn)一步擴(kuò)展
"0級(jí)"DOM
需要注意的是并沒(méi)有標(biāo)準(zhǔn)被稱為0級(jí)DOM匙奴,它僅是DOM歷史上一個(gè)參考點(diǎn)(0級(jí)DOM被認(rèn)為是在Internet Explorer 4.0 與Netscape Navigator4.0支持的最早的DHTML)
也就是說(shuō):
DOM 0級(jí)事件處理程序是 通過(guò)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,通過(guò)addEventListener(IE為attachEvent)去監(jiān)聽(tīng)事件,具體實(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àn)上文移除事件章節(jié)哗伯;
至于attachEvent與addEventListener的區(qū)別詳見(jiàn)后文IE兼容性
addEventListener的優(yōu)點(diǎn)是一個(gè)事件處理程序能對(duì)應(yīng)多個(gè)處理函數(shù)荒揣,缺點(diǎn)是存在兼容性問(wèn)題。
事件流的三種模型
所謂事件流焊刹,指的是頁(yè)面捕獲事件的順序系任,目前有三種模型:
IE的事件冒泡:當(dāng)發(fā)生事件時(shí),目標(biāo)節(jié)點(diǎn)先捕獲虐块,然后逐級(jí)向上傳播到父節(jié)點(diǎn)盈滴,即事件監(jiān)聽(tīng)處于冒泡階段
Netscape的事件捕獲:當(dāng)發(fā)生事件時(shí)苍柏,最先觸發(fā)父節(jié)點(diǎn)的事件監(jiān)聽(tīng)函數(shù)滓技,然后逐漸向下傳播到目標(biāo)節(jié)點(diǎn)隆嗅,即事件監(jiān)聽(tīng)處于捕獲階段
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)聽(tīng)的方法與addEventListener方法不同之處包括:
eventname必須包含on以及沒(méi)有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)