前言
JavaScript和HTML之間的交互是通過(guò)事件實(shí)現(xiàn)的??梢杂?strong>偵聽(tīng)器(或處理程序)來(lái)預(yù)訂事件。這種模式是設(shè)計(jì)模式中的觀察者模式(Observer Pattern)。使頁(yè)面行為和視圖之間形成松散耦合。
一、基本概念:事件處理程序
響應(yīng)某個(gè)事件的函數(shù)叫做事件處理程序(或事件偵聽(tīng)器),以on開(kāi)頭。
例如:onclick、onload等。
HTML事件處理程序
<input onclick="xx"/>
缺點(diǎn):
-
存在時(shí)差問(wèn)題。用戶可能會(huì)在HTML元素一出現(xiàn)頁(yè)面上就觸發(fā)響應(yīng)的事件,但當(dāng)時(shí)的事件處理程序有可能尚不具備執(zhí)行條件。
解決方案:將事件處理程序封裝在一個(gè)try-catch塊中。
這樣擴(kuò)展事件處理程序的作用域鏈在不同的瀏覽器中會(huì)導(dǎo)致不同的結(jié)果。
HTML與JavaScript代碼緊密耦合。
DOM0
以這種方式添加事件處理程序會(huì)在事件流的冒泡階段被處理。
element.onlick = function () {}
優(yōu)點(diǎn):
- 簡(jiǎn)單
- 具有跨瀏覽器的優(yōu)勢(shì)
刪除通過(guò)DOM0級(jí)方法指定的事件處理程序:
element.onlick = null;
DOM1
規(guī)定如何映射基于XML的文檔結(jié)構(gòu)
沒(méi)有涉及和事件相關(guān)的,在這里不贅述
DOM2
- 擴(kuò)充了眾多新類(lèi)型和新接口的定義
- DOM視圖:定義了跟蹤不同文檔視圖的接口
- DOM事件:定義了事件和事件處理的接口
- DOM樣式:增加了對(duì)CSS的支持
- DOM遍歷和范圍:遍歷和操作DOM的接口
- 通過(guò)
addEventListener()處理指定事件處理程序
element.addEventListener('click', function () {}, false)
false:在冒泡階段觸發(fā) (大多數(shù)情況下)
true:在捕獲階段觸發(fā)
優(yōu)點(diǎn):可以添加多個(gè)事件處理程序
- 通過(guò)
removeEventListener()刪除事件處理程序
element.remoteEventListener('click', function () {}, false)
注:移除時(shí)傳入的參數(shù)與添加處理程序時(shí)使用的參數(shù)相同。意味著通過(guò)addEventListener()添加的匿名函數(shù)無(wú)法被移除。
DOM3
- 添加了更多事件類(lèi)型:
- UI事件
- 鼠標(biāo)事件
- 滾輪事件
- 鍵盤(pán)事件
- 焦點(diǎn)事件
- 文本事件
- 合成事件
- 變動(dòng)事件
- 變動(dòng)名稱(chēng)事件
- 引入了以統(tǒng)一方式加載和保存文檔的方法:在DOM加載和保存模塊中定義
- 新增了驗(yàn)證文檔的方法:在DOM驗(yàn)證模塊中定義
- 支持XML1.0規(guī)范
- DOM3添加事件處理程序
element.addEventListener('keyup', function () {}, false)
二、DOM事件模型
- 捕獲
- 冒泡
三、DOM事件流
事件流描述的是從頁(yè)面中接收事件的順序。即瀏覽器在當(dāng)前頁(yè)面,用戶點(diǎn)擊了鼠標(biāo)左鍵,是怎么傳到頁(yè)面上的,又是如何響應(yīng)的呢?
DOM2級(jí)事件規(guī)定的事件流分三個(gè)階段:
- 捕獲階段:從上向下
- 目標(biāo)階段:事件通過(guò)捕獲到達(dá)目標(biāo)元素
- 冒泡階段:從目標(biāo)元素向上傳到window對(duì)象
四、描述DOM事件捕獲/冒泡的具體流程
事件捕獲會(huì)按照如下順序傳播:
- window
- document
- <html>
- <body>
- div 父級(jí)元素->子級(jí)元素
- 目標(biāo)元素
注:冒泡流程與之相反
五、事件對(duì)象的常見(jiàn)應(yīng)用
事件對(duì)象event包含所有與事件相關(guān)的信息,包括:導(dǎo)致事件的元素、事件的類(lèi)型以及其他信息。
5.1 DOM中的事件對(duì)象有關(guān)的屬性和方法
-
event.preventDefault()
類(lèi)型:Function
說(shuō)明:阻止特定事件的默認(rèn)行為
例子:給a標(biāo)簽綁定click事件,在響應(yīng)函數(shù)中設(shè)置該方法會(huì)阻止鏈接默認(rèn)跳轉(zhuǎn)的行為
-
event.stopPropagation()
類(lèi)型:Function
說(shuō)明:立即阻止事件進(jìn)一步冒泡
例子:父級(jí)元素和子元素分別綁定一個(gè)事件。我們希望子元素做一件事,父元素做一件事,即要使子元素單擊,父元素不響應(yīng)。
-
event.stopImmediatePropagation()
類(lèi)型:Function
說(shuō)明:阻止事件進(jìn)一步冒泡,同時(shí)阻止任何事件處理程序被調(diào)用。事件響應(yīng)優(yōu)先級(jí)(DOM3級(jí)事件中新增)
例子:按鈕綁定了兩個(gè)click事件,通過(guò)優(yōu)先級(jí)的方式,第一個(gè)響應(yīng)函數(shù)是a,第二個(gè)是b。以此注冊(cè)a,b兩個(gè)click事件。想a點(diǎn)擊時(shí)不要響應(yīng)b了,即在a響應(yīng)函數(shù)中定義這個(gè)方法,可以成功阻止b的執(zhí)行。
-
event.currentTarget/event.target(重點(diǎn))
類(lèi)型:Element
常見(jiàn)問(wèn)題:一個(gè)for循環(huán),給DOM注冊(cè)N多事件,即一個(gè)父元素有很多子元素,不想讓一個(gè)for循環(huán)綁定click事件到每一個(gè)子元素,該如何優(yōu)化?jQuery事件委托
事件委托(事件代理):子元素的事件代理都轉(zhuǎn)移到父級(jí)元素,綁定一次事件就可以了。響應(yīng)函數(shù)中要判斷當(dāng)前到底哪個(gè)元素被點(diǎn)擊了。
target:事件真正的目標(biāo),表示當(dāng)前被點(diǎn)擊的元素 (IE:SourceElement)
currentTarget:當(dāng)前正在處理事件的元素
注:在事件處理程序內(nèi)部,對(duì)象this始終等于currentTarget的值,而target則只包含事件的實(shí)際目標(biāo)。
5.2 事件委托
5.2.1 簡(jiǎn)述事件委托
關(guān)鍵:檢查事件是否來(lái)自你所預(yù)期的元素。
優(yōu)點(diǎn):
- 可以大量節(jié)省內(nèi)存占用,減少事件注冊(cè)
- 可以實(shí)現(xiàn)當(dāng)新增子對(duì)象時(shí)無(wú)需再次修改其綁定事件,對(duì)于動(dòng)態(tài)內(nèi)容部分尤為合適
5.2.2 代碼實(shí)現(xiàn)
基于第八部分做的封裝實(shí)現(xiàn)
var list = document.getElementById("myLinks");
EventUtil.addHander(list, "click", function (event) {
event = EventUtil.getEvent(event);
var target = EventUtil.getTarget(event);
switch (target.id) {
case "sayHello":
document.title = "Change the document's title to Hello World";
break;
case "doSearch":
location.;
break;
case "sayGoodbye":
alert("Bye~");
break;
}
});
六、自定義事件(模擬事件)
場(chǎng)景:有一個(gè)button,增加事件,在別的地方不用回調(diào)處理。
var eve=new Event('custome');
ev.addEventListener('custome', function(){
console.log('custome');
});
ev.dispatchEvent(eve);
CustomEvent :除了可以指定事件名,還可以指定參數(shù)
Event:只能指定事件名
七、捕獲流程/自定義事件
<div id="ev">
<style>
#ev{
width: 300px;
height: 100px;
background: yellow;
color: #fff;
text-align: center;
line-height: 100px;
}
</style>
目標(biāo)元素
<script type="text/javascript">
var ev=document.getElementById('ev');
//給window注冊(cè)捕獲事件
window.addEventListener('click',function(){
console.log('window capture');
},true);
document.addEventListener('click',function(){
console.log('document capture');
},true);
document.documentElement.addEventListener('click',function(){
console.log('body capture');
},true);
document.body.addEventListener('click',function(){
console.log('body capture');
},true);
ev.addEventListener('click',function(){
console.log('ev capture');
},true);
//自定義事件
var eve=new Event('test');
ev.addEventListener('test',function(){
console.log('test dispatch');
})
//延時(shí)器模擬某個(gè)按鈕的動(dòng)作
setTimeout(function(){
ev.dispatchEvent(eve);
},1000);
</script>
</div>
注:
響應(yīng)的順序和定義的順序無(wú)關(guān)
把true改成false 則為冒泡
八、手寫(xiě)DOM事件處理跨瀏覽器(兼容IE)封裝
var eventUtil = { /*DOM2級(jí)事件處理程序*/
//給一個(gè)元素添加事件
addHander: function (element, type, hander) {
if (element.addEventListener) {
element.addEventListener(type, hander, false); /*非IE*/
} else if (element.attachEvent) {
element.attachEvent("on" + type, hander); /*IE*/
} else {
element["on" + type] = hander;
}
},
//給一個(gè)元素刪除事件
removeHander: function (element, type, hander) {
if (element.removeEventListener) {
element.removeEventListener(type, hander, false); /*非IE*/
} else if (element.detachEvent) {
element.detachEvent("on" + type, hander); /*IE*/
} else {
element["on" + type] = null;
}
},
//獲取兼容所有瀏覽器的一個(gè)對(duì)象
getEvent: function (event) {
return event ? event : window.event;
},
//獲取事件類(lèi)型
getType: function (event) { //此項(xiàng)不存在瀏覽器兼容問(wèn)題
return event.type;
},
//事件來(lái)自哪個(gè)元素
getElement: function (event) {
return event.target || event.srcElement;
},
//阻止事件默認(rèn)行為
preventDefault: function (event) {
if (event.preventDefault) {
event.preventDefault(); /*非IE*/
} else {
event.returnValue = false; /*IE*/
}
},
//阻止事件冒泡
stopPropagation: function (event) {
if (event.stopPropagation) {
event.stopPropagation(); /*非IE*/
} else {
event.cancelBubble = true; /*IE*/
}
}
}
以上,如有錯(cuò)誤希望大家指正。