JavaScript Event 對(duì)象

2023-03-20 15:47 更新

概述

事件發(fā)生以后,會(huì)產(chǎn)生一個(gè)事件對(duì)象,作為參數(shù)傳給監(jiān)聽函數(shù)。瀏覽器原生提供一個(gè)Event對(duì)象,所有的事件都是這個(gè)對(duì)象的實(shí)例,或者說繼承了Event.prototype對(duì)象。

Event對(duì)象本身就是一個(gè)構(gòu)造函數(shù),可以用來生成新的實(shí)例。

event = new Event(type, options);

Event構(gòu)造函數(shù)接受兩個(gè)參數(shù)。第一個(gè)參數(shù)type是字符串,表示事件的名稱;第二個(gè)參數(shù)options是一個(gè)對(duì)象,表示事件對(duì)象的配置。該對(duì)象主要有下面兩個(gè)屬性。

  • bubbles:布爾值,可選,默認(rèn)為false,表示事件對(duì)象是否冒泡。
  • cancelable:布爾值,可選,默認(rèn)為false,表示事件是否可以被取消,即能否用Event.preventDefault()取消這個(gè)事件。一旦事件被取消,就好像從來沒有發(fā)生過,不會(huì)觸發(fā)瀏覽器對(duì)該事件的默認(rèn)行為。
var ev = new Event(
  'look',
  {
    'bubbles': true,
    'cancelable': false
  }
);
document.dispatchEvent(ev);

上面代碼新建一個(gè)look事件實(shí)例,然后使用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ā)出一個(gè)click事件,該事件默認(rèn)不會(huì)冒泡。div.addEventListener方法指定在冒泡階段監(jiān)聽,因此監(jiān)聽函數(shù)不會(huì)觸發(fā)。如果寫成div.addEventListener('click', callback, true),那么在“捕獲階段”可以監(jiān)聽到這個(gè)事件。

另一方面,如果這個(gè)事件在div元素上觸發(fā)。

div.dispatchEvent(click);

那么,不管div元素是在冒泡階段監(jiān)聽,還是在捕獲階段監(jiān)聽,都會(huì)觸發(fā)監(jiān)聽函數(shù)。因?yàn)檫@時(shí)div元素是事件的目標(biāo),不存在是否冒泡的問題,div元素總是會(huì)接收到事件,因此導(dǎo)致監(jiān)聽函數(shù)生效。

實(shí)例屬性

Event.bubbles,Event.eventPhase

Event.bubbles屬性返回一個(gè)布爾值,表示當(dāng)前事件是否會(huì)冒泡。該屬性為只讀屬性,一般用來了解 Event 實(shí)例是否可以冒泡。前面說過,除非顯式聲明,Event構(gòu)造函數(shù)生成的事件,默認(rèn)是不冒泡的。

Event.eventPhase屬性返回一個(gè)整數(shù)常量,表示事件目前所處的階段。該屬性只讀。

var phase = event.eventPhase;

Event.eventPhase的返回值有四種可能。

  • 0,事件目前沒有發(fā)生。
  • 1,事件目前處于捕獲階段,即處于從祖先節(jié)點(diǎn)向目標(biāo)節(jié)點(diǎn)的傳播過程中。
  • 2,事件到達(dá)目標(biāo)節(jié)點(diǎn),即Event.target屬性指向的那個(gè)節(jié)點(diǎn)。
  • 3,事件處于冒泡階段,即處于從目標(biāo)節(jié)點(diǎn)向祖先節(jié)點(diǎn)的反向傳播過程中。

Event.cancelable,Event.cancelBubble,event.defaultPrevented #

Event.cancelable屬性返回一個(gè)布爾值,表示事件是否可以取消。該屬性為只讀屬性,一般用來了解 Event 實(shí)例的特性。

大多數(shù)瀏覽器的原生事件是可以取消的。比如,取消click事件,點(diǎn)擊鏈接將無效。但是除非顯式聲明,Event構(gòu)造函數(shù)生成的事件,默認(rèn)是不可以取消的。

var evt = new Event('foo');
evt.cancelable  // false

當(dāng)Event.cancelable屬性為true時(shí),調(diào)用Event.preventDefault()就可以取消這個(gè)事件,阻止瀏覽器對(duì)該事件的默認(rèn)行為。

如果事件不能取消,調(diào)用Event.preventDefault()會(huì)沒有任何效果。所以使用這個(gè)方法之前,最好用Event.cancelable屬性判斷一下是否可以取消。

function preventEvent(event) {
  if (event.cancelable) {
    event.preventDefault();
  } else {
    console.warn('This event couldn\'t be canceled.');
    console.dir(event);
  }
}

Event.cancelBubble屬性是一個(gè)布爾值,如果設(shè)為true,相當(dāng)于執(zhí)行Event.stopPropagation(),可以阻止事件的傳播。

Event.defaultPrevented屬性返回一個(gè)布爾值,表示該事件是否調(diào)用過Event.preventDefault方法。該屬性只讀。

if (event.defaultPrevented) {
  console.log('該事件已經(jīng)取消了');
}

Event.currentTarget,Event.target

事件發(fā)生以后,會(huì)經(jīng)過捕獲和冒泡兩個(gè)階段,依次通過多個(gè) DOM 節(jié)點(diǎn)。因此,任意事件都有兩個(gè)與事件相關(guān)的節(jié)點(diǎn),一個(gè)是事件的原始觸發(fā)節(jié)點(diǎn)(Event.target),另一個(gè)是事件當(dāng)前正在通過的節(jié)點(diǎn)(Event.currentTarget)。前者通常是后者的后代節(jié)點(diǎn)。

Event.currentTarget屬性返回事件當(dāng)前所在的節(jié)點(diǎn),即事件當(dāng)前正在通過的節(jié)點(diǎn),也就是當(dāng)前正在執(zhí)行的監(jiān)聽函數(shù)所在的那個(gè)節(jié)點(diǎn)。隨著事件的傳播,這個(gè)屬性的值會(huì)變。

Event.target屬性返回原始觸發(fā)事件的那個(gè)節(jié)點(diǎn),即事件最初發(fā)生的節(jié)點(diǎn)。這個(gè)屬性不會(huì)隨著事件的傳播而改變。

事件傳播過程中,不同節(jié)點(diǎn)的監(jiān)聽函數(shù)內(nèi)部的Event.targetEvent.currentTarget屬性的值是不一樣的。

// HTML 代碼為
// <p id="para">Hello <em>World</em></p>
function hide(e) {
  // 不管點(diǎn)擊 Hello 或 World,總是返回 true
  console.log(this === e.currentTarget);

  // 點(diǎn)擊 Hello,返回 true
  // 點(diǎn)擊 World,返回 false
  console.log(this === e.target);
}

document.getElementById('para').addEventListener('click', hide, false);

上面代碼中,<em><p>的子節(jié)點(diǎn),點(diǎn)擊<em>或者點(diǎn)擊<p>,都會(huì)導(dǎo)致監(jiān)聽函數(shù)執(zhí)行。這時(shí),e.target總是指向原始點(diǎn)擊位置的那個(gè)節(jié)點(diǎn),而e.currentTarget指向事件傳播過程中正在經(jīng)過的那個(gè)節(jié)點(diǎn)。由于監(jiān)聽函數(shù)只有事件經(jīng)過時(shí)才會(huì)觸發(fā),所以e.currentTarget總是等同于監(jiān)聽函數(shù)內(nèi)部的this。

Event.type

Event.type屬性返回一個(gè)字符串,表示事件類型。事件的類型是在生成事件的時(shí)候指定的。該屬性只讀。

var evt = new Event('foo');
evt.type // "foo"

Event.timeStamp

Event.timeStamp屬性返回一個(gè)毫秒時(shí)間戳,表示事件發(fā)生的時(shí)間。它是相對(duì)于網(wǎng)頁加載成功開始計(jì)算的。

var evt = new Event('foo');
evt.timeStamp // 3683.6999999995896

它的返回值有可能是整數(shù),也有可能是小數(shù)(高精度時(shí)間戳),取決于瀏覽器的設(shè)置。

下面是一個(gè)計(jì)算鼠標(biāo)移動(dòng)速度的例子,顯示每秒移動(dòng)的像素?cái)?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屬性返回一個(gè)布爾值,表示該事件是否由真實(shí)的用戶行為產(chǎn)生。比如,用戶點(diǎn)擊鏈接會(huì)產(chǎn)生一個(gè)click事件,該事件是用戶產(chǎn)生的;Event構(gòu)造函數(shù)生成的事件,則是腳本產(chǎn)生的。

var evt = new Event('foo');
evt.isTrusted // false

上面代碼中,evt對(duì)象是腳本產(chǎn)生的,所以isTrusted屬性返回false

Event.detail

Event.detail屬性只有瀏覽器的 UI (用戶界面)事件才具有。該屬性返回一個(gè)數(shù)值,表示事件的某種信息。具體含義與事件類型相關(guān)。比如,對(duì)于clickdblclick事件,Event.detail是鼠標(biāo)按下的次數(shù)(1表示單擊,2表示雙擊,3表示三擊);對(duì)于鼠標(biāo)滾輪事件,Event.detail是滾輪正向滾動(dòng)的距離,負(fù)值就是負(fù)向滾動(dòng)的距離,返回值總是3的倍數(shù)。

// HTML 代碼如下
// <p>Hello</p>
function giveDetails(e) {
  console.log(e.detail);
}

document.querySelector('p').onclick = giveDetails;

實(shí)例方法

Event.preventDefault()

Event.preventDefault方法取消瀏覽器對(duì)當(dāng)前事件的默認(rèn)行為。比如點(diǎn)擊鏈接后,瀏覽器默認(rèn)會(huì)跳轉(zhuǎn)到另一個(gè)頁面,使用這個(gè)方法以后,就不會(huì)跳轉(zhuǎn)了;再比如,按一下空格鍵,頁面向下滾動(dòng)一段距離,使用這個(gè)方法以后也不會(huì)滾動(dòng)了。該方法生效的前提是,事件對(duì)象的cancelable屬性為true,如果為false,調(diào)用該方法沒有任何效果。

注意,該方法只是取消事件對(duì)當(dāng)前元素的默認(rèn)影響,不會(huì)阻止事件的傳播。如果要阻止傳播,可以使用stopPropagation()stopImmediatePropagation()方法。

// HTML 代碼為
// <input type="checkbox" id="my-checkbox" />
var cb = document.getElementById('my-checkbox');

cb.addEventListener(
  'click',
  function (e){ e.preventDefault(); },
  false
);

上面代碼中,瀏覽器的默認(rèn)行為是單擊會(huì)選中單選框,取消這個(gè)行為,就導(dǎo)致無法選中單選框。

利用這個(gè)方法,可以為文本輸入框設(shè)置校驗(yàn)條件。如果用戶的輸入不符合條件,就無法將字符輸入文本框。

// 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ù)后,將只能輸入小寫字母,否則輸入事件的默認(rèn)行為(寫入文本框)將被取消,導(dǎo)致不能向文本框輸入內(nèi)容。

Event.stopPropagation()

stopPropagation方法阻止事件在 DOM 中繼續(xù)傳播,防止再觸發(fā)定義在別的節(jié)點(diǎn)上的監(jiān)聽函數(shù),但是不包括在當(dāng)前節(jié)點(diǎn)上其他的事件監(jiān)聽函數(shù)。

function stopEvent(e) {
  e.stopPropagation();
}

el.addEventListener('click', stopEvent, false);

上面代碼中,click事件將不會(huì)進(jìn)一步冒泡到el節(jié)點(diǎn)的父節(jié)點(diǎn)。

Event.stopImmediatePropagation()

Event.stopImmediatePropagation方法阻止同一個(gè)事件的其他監(jiān)聽函數(shù)被調(diào)用,不管監(jiān)聽函數(shù)定義在當(dāng)前節(jié)點(diǎn)還是其他節(jié)點(diǎn)。也就是說,該方法阻止事件的傳播,比Event.stopPropagation()更徹底。

如果同一個(gè)節(jié)點(diǎn)對(duì)于同一個(gè)事件指定了多個(gè)監(jiān)聽函數(shù),這些函數(shù)會(huì)根據(jù)添加的順序依次調(diào)用。只要其中有一個(gè)監(jiān)聽函數(shù)調(diào)用了Event.stopImmediatePropagation方法,其他的監(jiān)聽函數(shù)就不會(huì)再執(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é)點(diǎn)上,為click事件添加了兩個(gè)監(jiān)聽函數(shù)l1l2。由于l1調(diào)用了event.stopImmediatePropagation方法,所以l2不會(huì)被調(diào)用。

Event.composedPath()

Event.composedPath()返回一個(gè)數(shù)組,成員是事件的最底層節(jié)點(diǎn)和依次冒泡經(jīng)過的所有上層節(jié)點(diǎn)。

// 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é)點(diǎn)是p,向上依次是div、bodyhtml、document、Window。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)