概述
事件發(fā)生以后,會產(chǎn)生一個事件對象,作為參數(shù)傳給監(jiān)聽函數(shù)。瀏覽器原生提供一個Event
對象,所有的事件都是這個對象的實例,或者說繼承了Event.prototype
對象。
Event
對象本身就是一個構(gòu)造函數(shù),可以用來生成新的實例。
event = new Event(type, options);
Event
構(gòu)造函數(shù)接受兩個參數(shù)。第一個參數(shù)type
是字符串,表示事件的名稱;第二個參數(shù)options
是一個對象,表示事件對象的配置。該對象主要有下面兩個屬性。
bubbles
:布爾值,可選,默認為false
,表示事件對象是否冒泡。cancelable
:布爾值,可選,默認為false
,表示事件是否可以被取消,即能否用Event.preventDefault()
取消這個事件。一旦事件被取消,就好像從來沒有發(fā)生過,不會觸發(fā)瀏覽器對該事件的默認行為。
var ev = new Event(
'look',
{
'bubbles': true,
'cancelable': false
}
);
document.dispatchEvent(ev);
上面代碼新建一個look
事件實例,然后使用dispatchEvent
方法觸發(fā)該事件。
注意,如果不是顯式指定bubbles
屬性為true
,生成的事件就只能在“捕獲階段”觸發(fā)監(jiān)聽函數(shù)。
// HTML 代碼為
// <div><p>Hello</p></div>
var div = document.querySelector('div');
var p = document.querySelector('p');
function callback(event) {
var tag = event.currentTarget.tagName;
console.log('Tag: ' + tag); // 沒有任何輸出
}
div.addEventListener('click', callback, false);
var click = new Event('click');
p.dispatchEvent(click);
上面代碼中,p
元素發(fā)出一個click
事件,該事件默認不會冒泡。div.addEventListener
方法指定在冒泡階段監(jiān)聽,因此監(jiān)聽函數(shù)不會觸發(fā)。如果寫成div.addEventListener('click', callback, true)
,那么在“捕獲階段”可以監(jiān)聽到這個事件。
另一方面,如果這個事件在div
元素上觸發(fā)。
div.dispatchEvent(click);
那么,不管div
元素是在冒泡階段監(jiān)聽,還是在捕獲階段監(jiān)聽,都會觸發(fā)監(jiān)聽函數(shù)。因為這時div
元素是事件的目標(biāo),不存在是否冒泡的問題,div
元素總是會接收到事件,因此導(dǎo)致監(jiān)聽函數(shù)生效。
實例屬性
Event.bubbles,Event.eventPhase
Event.bubbles
屬性返回一個布爾值,表示當(dāng)前事件是否會冒泡。該屬性為只讀屬性,一般用來了解 Event 實例是否可以冒泡。前面說過,除非顯式聲明,Event
構(gòu)造函數(shù)生成的事件,默認是不冒泡的。
Event.eventPhase
屬性返回一個整數(shù)常量,表示事件目前所處的階段。該屬性只讀。
var phase = event.eventPhase;
Event.eventPhase
的返回值有四種可能。
- 0,事件目前沒有發(fā)生。
- 1,事件目前處于捕獲階段,即處于從祖先節(jié)點向目標(biāo)節(jié)點的傳播過程中。
- 2,事件到達目標(biāo)節(jié)點,即
Event.target
屬性指向的那個節(jié)點。 - 3,事件處于冒泡階段,即處于從目標(biāo)節(jié)點向祖先節(jié)點的反向傳播過程中。
Event.cancelable,Event.cancelBubble,event.defaultPrevented #
Event.cancelable
屬性返回一個布爾值,表示事件是否可以取消。該屬性為只讀屬性,一般用來了解 Event 實例的特性。
大多數(shù)瀏覽器的原生事件是可以取消的。比如,取消click
事件,點擊鏈接將無效。但是除非顯式聲明,Event
構(gòu)造函數(shù)生成的事件,默認是不可以取消的。
var evt = new Event('foo');
evt.cancelable // false
當(dāng)Event.cancelable
屬性為true
時,調(diào)用Event.preventDefault()
就可以取消這個事件,阻止瀏覽器對該事件的默認行為。
如果事件不能取消,調(diào)用Event.preventDefault()
會沒有任何效果。所以使用這個方法之前,最好用Event.cancelable
屬性判斷一下是否可以取消。
function preventEvent(event) {
if (event.cancelable) {
event.preventDefault();
} else {
console.warn('This event couldn\'t be canceled.');
console.dir(event);
}
}
Event.cancelBubble
屬性是一個布爾值,如果設(shè)為true
,相當(dāng)于執(zhí)行Event.stopPropagation()
,可以阻止事件的傳播。
Event.defaultPrevented
屬性返回一個布爾值,表示該事件是否調(diào)用過Event.preventDefault
方法。該屬性只讀。
if (event.defaultPrevented) {
console.log('該事件已經(jīng)取消了');
}
Event.currentTarget,Event.target
事件發(fā)生以后,會經(jīng)過捕獲和冒泡兩個階段,依次通過多個 DOM 節(jié)點。因此,任意事件都有兩個與事件相關(guān)的節(jié)點,一個是事件的原始觸發(fā)節(jié)點(Event.target
),另一個是事件當(dāng)前正在通過的節(jié)點(Event.currentTarget
)。前者通常是后者的后代節(jié)點。
Event.currentTarget
屬性返回事件當(dāng)前所在的節(jié)點,即事件當(dāng)前正在通過的節(jié)點,也就是當(dāng)前正在執(zhí)行的監(jiān)聽函數(shù)所在的那個節(jié)點。隨著事件的傳播,這個屬性的值會變。
Event.target
屬性返回原始觸發(fā)事件的那個節(jié)點,即事件最初發(fā)生的節(jié)點。這個屬性不會隨著事件的傳播而改變。
事件傳播過程中,不同節(jié)點的監(jiān)聽函數(shù)內(nèi)部的Event.target
與Event.currentTarget
屬性的值是不一樣的。
// HTML 代碼為
// <p id="para">Hello <em>World</em></p>
function hide(e) {
// 不管點擊 Hello 或 World,總是返回 true
console.log(this === e.currentTarget);
// 點擊 Hello,返回 true
// 點擊 World,返回 false
console.log(this === e.target);
}
document.getElementById('para').addEventListener('click', hide, false);
上面代碼中,<em>
是<p>
的子節(jié)點,點擊<em>
或者點擊<p>
,都會導(dǎo)致監(jiān)聽函數(shù)執(zhí)行。這時,e.target
總是指向原始點擊位置的那個節(jié)點,而e.currentTarget
指向事件傳播過程中正在經(jīng)過的那個節(jié)點。由于監(jiān)聽函數(shù)只有事件經(jīng)過時才會觸發(fā),所以e.currentTarget
總是等同于監(jiān)聽函數(shù)內(nèi)部的this
。
Event.type
Event.type
屬性返回一個字符串,表示事件類型。事件的類型是在生成事件的時候指定的。該屬性只讀。
var evt = new Event('foo');
evt.type // "foo"
Event.timeStamp
Event.timeStamp
屬性返回一個毫秒時間戳,表示事件發(fā)生的時間。它是相對于網(wǎng)頁加載成功開始計算的。
var evt = new Event('foo');
evt.timeStamp // 3683.6999999995896
它的返回值有可能是整數(shù),也有可能是小數(shù)(高精度時間戳),取決于瀏覽器的設(shè)置。
下面是一個計算鼠標(biāo)移動速度的例子,顯示每秒移動的像素數(shù)量。
var previousX;
var previousY;
var previousT;
window.addEventListener('mousemove', function(event) {
if (
previousX !== undefined &&
previousY !== undefined &&
previousT !== undefined
) {
var deltaX = event.screenX - previousX;
var deltaY = event.screenY - previousY;
var deltaD = Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));
var deltaT = event.timeStamp - previousT;
console.log(deltaD / deltaT * 1000);
}
previousX = event.screenX;
previousY = event.screenY;
previousT = event.timeStamp;
});
Event.isTrusted
Event.isTrusted
屬性返回一個布爾值,表示該事件是否由真實的用戶行為產(chǎn)生。比如,用戶點擊鏈接會產(chǎn)生一個click
事件,該事件是用戶產(chǎn)生的;Event
構(gòu)造函數(shù)生成的事件,則是腳本產(chǎn)生的。
var evt = new Event('foo');
evt.isTrusted // false
上面代碼中,evt
對象是腳本產(chǎn)生的,所以isTrusted
屬性返回false
。
Event.detail
Event.detail
屬性只有瀏覽器的 UI (用戶界面)事件才具有。該屬性返回一個數(shù)值,表示事件的某種信息。具體含義與事件類型相關(guān)。比如,對于click
和dblclick
事件,Event.detail
是鼠標(biāo)按下的次數(shù)(1
表示單擊,2
表示雙擊,3
表示三擊);對于鼠標(biāo)滾輪事件,Event.detail
是滾輪正向滾動的距離,負值就是負向滾動的距離,返回值總是3的倍數(shù)。
// HTML 代碼如下
// <p>Hello</p>
function giveDetails(e) {
console.log(e.detail);
}
document.querySelector('p').onclick = giveDetails;
實例方法
Event.preventDefault()
Event.preventDefault
方法取消瀏覽器對當(dāng)前事件的默認行為。比如點擊鏈接后,瀏覽器默認會跳轉(zhuǎn)到另一個頁面,使用這個方法以后,就不會跳轉(zhuǎn)了;再比如,按一下空格鍵,頁面向下滾動一段距離,使用這個方法以后也不會滾動了。該方法生效的前提是,事件對象的cancelable
屬性為true
,如果為false
,調(diào)用該方法沒有任何效果。
注意,該方法只是取消事件對當(dāng)前元素的默認影響,不會阻止事件的傳播。如果要阻止傳播,可以使用stopPropagation()
或stopImmediatePropagation()
方法。
// HTML 代碼為
// <input type="checkbox" id="my-checkbox" />
var cb = document.getElementById('my-checkbox');
cb.addEventListener(
'click',
function (e){ e.preventDefault(); },
false
);
上面代碼中,瀏覽器的默認行為是單擊會選中單選框,取消這個行為,就導(dǎo)致無法選中單選框。
利用這個方法,可以為文本輸入框設(shè)置校驗條件。如果用戶的輸入不符合條件,就無法將字符輸入文本框。
// HTML 代碼為
// <input type="text" id="my-input" />
var input = document.getElementById('my-input');
input.addEventListener('keypress', checkName, false);
function checkName(e) {
if (e.charCode < 97 || e.charCode > 122) {
e.preventDefault();
}
}
上面代碼為文本框的keypress
事件設(shè)定監(jiān)聽函數(shù)后,將只能輸入小寫字母,否則輸入事件的默認行為(寫入文本框)將被取消,導(dǎo)致不能向文本框輸入內(nèi)容。
Event.stopPropagation()
stopPropagation
方法阻止事件在 DOM 中繼續(xù)傳播,防止再觸發(fā)定義在別的節(jié)點上的監(jiān)聽函數(shù),但是不包括在當(dāng)前節(jié)點上其他的事件監(jiān)聽函數(shù)。
function stopEvent(e) {
e.stopPropagation();
}
el.addEventListener('click', stopEvent, false);
上面代碼中,click
事件將不會進一步冒泡到el
節(jié)點的父節(jié)點。
Event.stopImmediatePropagation()
Event.stopImmediatePropagation
方法阻止同一個事件的其他監(jiān)聽函數(shù)被調(diào)用,不管監(jiān)聽函數(shù)定義在當(dāng)前節(jié)點還是其他節(jié)點。也就是說,該方法阻止事件的傳播,比Event.stopPropagation()
更徹底。
如果同一個節(jié)點對于同一個事件指定了多個監(jiān)聽函數(shù),這些函數(shù)會根據(jù)添加的順序依次調(diào)用。只要其中有一個監(jiān)聽函數(shù)調(diào)用了Event.stopImmediatePropagation
方法,其他的監(jiān)聽函數(shù)就不會再執(zhí)行了。
function l1(e){
e.stopImmediatePropagation();
}
function l2(e){
console.log('hello world');
}
el.addEventListener('click', l1, false);
el.addEventListener('click', l2, false);
上面代碼在el
節(jié)點上,為click
事件添加了兩個監(jiān)聽函數(shù)l1
和l2
。由于l1
調(diào)用了event.stopImmediatePropagation
方法,所以l2
不會被調(diào)用。
Event.composedPath()
Event.composedPath()
返回一個數(shù)組,成員是事件的最底層節(jié)點和依次冒泡經(jīng)過的所有上層節(jié)點。
// HTML 代碼如下
// <div>
// <p>Hello</p>
// </div>
var div = document.querySelector('div');
var p = document.querySelector('p');
div.addEventListener('click', function (e) {
console.log(e.composedPath());
}, false);
// [p, div, body, html, document, Window]
上面代碼中,click
事件的最底層節(jié)點是p
,向上依次是div
、body
、html
、document
、Window
。
更多建議: