事件的本質(zhì)是程序各個(gè)組成部分之間的一種通信方式,也是異步編程的一種實(shí)現(xiàn)。DOM 支持大量的事件,本章開始介紹 DOM 的事件編程。
介紹具體的事件之前,先來看看如何讓 DOM 節(jié)點(diǎn)監(jiān)聽事件。
概述
DOM 節(jié)點(diǎn)的事件操作(監(jiān)聽和觸發(fā)),都定義在EventTarget
接口。所有節(jié)點(diǎn)對(duì)象都部署了這個(gè)接口,其他一些需要事件通信的瀏覽器內(nèi)置對(duì)象(比如,XMLHttpRequest
、AudioNode
、AudioContext
)也部署了這個(gè)接口。
該接口主要提供三個(gè)實(shí)例方法。
addEventListener()
:綁定事件的監(jiān)聽函數(shù)removeEventListener()
:移除事件的監(jiān)聽函數(shù)dispatchEvent()
:觸發(fā)事件
EventTarget.addEventListener()
EventTarget.addEventListener()
用于在當(dāng)前節(jié)點(diǎn)或?qū)ο笊希床渴鹆?EventTarget 接口的對(duì)象),定義一個(gè)特定事件的監(jiān)聽函數(shù)。一旦這個(gè)事件發(fā)生,就會(huì)執(zhí)行監(jiān)聽函數(shù)。該方法沒有返回值。
target.addEventListener(type, listener[, useCapture]);
該方法接受三個(gè)參數(shù)。
type
:事件名稱,大小寫敏感。listener
:監(jiān)聽函數(shù)。事件發(fā)生時(shí),會(huì)調(diào)用該監(jiān)聽函數(shù)。useCapture
:布爾值,如果設(shè)為true
,表示監(jiān)聽函數(shù)將在捕獲階段(capture)觸發(fā)(參見后文《事件的傳播》部分)。該參數(shù)可選,默認(rèn)值為false
(監(jiān)聽函數(shù)只在冒泡階段被觸發(fā))。
下面是一個(gè)例子。
function hello() {
console.log('Hello world');
}
var button = document.getElementById('btn');
button.addEventListener('click', hello, false);
上面代碼中,button
節(jié)點(diǎn)的addEventListener()
方法綁定click
事件的監(jiān)聽函數(shù)hello()
,該函數(shù)只在冒泡階段觸發(fā)。
關(guān)于參數(shù),有兩個(gè)地方需要注意。
首先,第二個(gè)參數(shù)除了監(jiān)聽函數(shù),還可以是一個(gè)具有handleEvent
方法的對(duì)象,效果與監(jiān)聽函數(shù)一樣。
buttonElement.addEventListener('click', {
handleEvent: function (event) {
console.log('click');
}
});
上面代碼中,addEventListener()
方法的第二個(gè)參數(shù),就是一個(gè)具有handleEvent()
方法的對(duì)象。
其次,第三個(gè)參數(shù)除了布爾值useCapture
,還可以是一個(gè)監(jiān)聽器配置對(duì)象,定制事件監(jiān)聽行為。該對(duì)象有以下屬性。
capture
:布爾值,如果設(shè)為true
,表示監(jiān)聽函數(shù)在捕獲階段觸發(fā),默認(rèn)為false
,在冒泡階段觸發(fā)。once
:布爾值,如果設(shè)為true
,表示監(jiān)聽函數(shù)執(zhí)行一次就會(huì)自動(dòng)移除,后面將不再監(jiān)聽該事件。該屬性默認(rèn)值為false
。passive
:布爾值,設(shè)為true
時(shí),表示禁止監(jiān)聽函數(shù)調(diào)用preventDefault()
方法。如果調(diào)用了,瀏覽器將忽略這個(gè)要求,并在控制臺(tái)輸出一條警告。該屬性默認(rèn)值為false
。signal
:該屬性的值為一個(gè) AbortSignal 對(duì)象,為監(jiān)聽器設(shè)置了一個(gè)信號(hào)通道,用來在需要時(shí)發(fā)出信號(hào),移除監(jiān)聽函數(shù)。
下面是once
屬性的例子,讓監(jiān)聽函數(shù)只執(zhí)行一次。
element.addEventListener('click', function (event) {
// 只執(zhí)行一次的代碼
}, {once: true});
addEventListener()
方法可以為針對(duì)當(dāng)前對(duì)象的同一個(gè)事件,添加多個(gè)不同的監(jiān)聽函數(shù)。這些函數(shù)按照添加順序觸發(fā),即先添加先觸發(fā)。如果為同一個(gè)事件多次添加同一個(gè)監(jiān)聽函數(shù),該函數(shù)只會(huì)執(zhí)行一次,多余的添加將自動(dòng)被去除(不必使用removeEventListener()
方法手動(dòng)去除)。
function hello() {
console.log('Hello world');
}
document.addEventListener('click', hello, false);
document.addEventListener('click', hello, false);
執(zhí)行上面代碼,點(diǎn)擊文檔只會(huì)輸出一行Hello world
。
如果希望向監(jiān)聽函數(shù)傳遞參數(shù),可以用匿名函數(shù)包裝一下監(jiān)聽函數(shù)。
function print(x) {
console.log(x);
}
var el = document.getElementById('div1');
el.addEventListener('click', function () { print('Hello'); }, false);
上面代碼通過匿名函數(shù),向監(jiān)聽函數(shù)print
傳遞了一個(gè)參數(shù)。
監(jiān)聽函數(shù)內(nèi)部的this
,指向當(dāng)前事件所在的那個(gè)對(duì)象。
// HTML 代碼如下
// <p id="para">Hello</p>
var para = document.getElementById('para');
para.addEventListener('click', function (e) {
console.log(this.nodeName); // "P"
}, false);
上面代碼中,監(jiān)聽函數(shù)內(nèi)部的this
指向事件所在的對(duì)象para
。
EventTarget.removeEventListener()
EventTarget.removeEventListener()
方法用來移除addEventListener()
方法添加的事件監(jiān)聽函數(shù)。該方法沒有返回值。
div.addEventListener('click', listener, false);
div.removeEventListener('click', listener, false);
removeEventListener()
方法的參數(shù),與addEventListener()
方法完全一致。它的第一個(gè)參數(shù)“事件類型”,大小寫敏感。
注意,removeEventListener()
方法移除的監(jiān)聽函數(shù),必須是addEventListener()
方法添加的那個(gè)監(jiān)聽函數(shù),而且必須在同一個(gè)元素節(jié)點(diǎn),否則無效。
div.addEventListener('click', function (e) {}, false);
div.removeEventListener('click', function (e) {}, false);
上面代碼中,removeEventListener()
方法無效,因?yàn)楸O(jiān)聽函數(shù)不是同一個(gè)匿名函數(shù)。
element.addEventListener('mousedown', handleMouseDown, true);
element.removeEventListener("mousedown", handleMouseDown, false);
上面代碼中,removeEventListener()
方法也是無效的,因?yàn)榈谌齻€(gè)參數(shù)不一樣。
EventTarget.dispatchEvent()
EventTarget.dispatchEvent()
方法在當(dāng)前節(jié)點(diǎn)上觸發(fā)指定事件,從而觸發(fā)監(jiān)聽函數(shù)的執(zhí)行。該方法返回一個(gè)布爾值,只要有一個(gè)監(jiān)聽函數(shù)調(diào)用了Event.preventDefault()
,則返回值為false
,否則為true
。
target.dispatchEvent(event)
dispatchEvent()
方法的參數(shù)是一個(gè)Event
對(duì)象的實(shí)例(詳見《Event 對(duì)象》章節(jié))。
para.addEventListener('click', hello, false);
var event = new Event('click');
para.dispatchEvent(event);
上面代碼在當(dāng)前節(jié)點(diǎn)觸發(fā)了click
事件。
如果dispatchEvent()
方法的參數(shù)為空,或者不是一個(gè)有效的事件對(duì)象,將報(bào)錯(cuò)。
下面代碼根據(jù)dispatchEvent()
方法的返回值,判斷事件是否被取消了。
var canceled = !cb.dispatchEvent(event);
if (canceled) {
console.log('事件取消');
} else {
console.log('事件未取消');
}
更多建議: