1. 程式人生 > >[js點滴]JavaScript事件Event物件03

[js點滴]JavaScript事件Event物件03

Event物件

事件發生以後,會生成一個事件物件,作為引數傳給監聽函式。瀏覽器原生提供一個Event物件,所有的事件都是這個物件的例項,或者說繼承了Event.prototype物件。

Event物件本身就是一個建構函式,可以用來生成新的例項。

event = new Event(typeArg, eventInit);

Event建構函式接受兩個引數。第一個引數是字串,表示事件的名稱;第二個引數是一個物件,表示事件物件的配置。該引數可以有以下兩個屬性。

  • bubbles:布林值,可選,預設為false,表示事件物件是否冒泡。

  • cancelable:布林值,可選,預設為false,表示事件是否可以被取消。

var ev = new Event("look", {"bubbles":true, "cancelable":false});
document.dispatchEvent(ev);

上面程式碼新建一個look事件例項,然後使用dispatchEvent方法觸發該事件。

IE8及以下版本,事件物件不作為引數傳遞,而是通過window物件的event屬性讀取,並且事件物件的target屬性叫做srcElement屬性。所以,以前獲取事件資訊,往往要寫成下面這樣。

function myEventHandler(event) {
  var actualEvent = event
|| window.event; var actualTarget = actualEvent.target || actualEvent.srcElement; // ... }

上面的程式碼只是為了說明以前的程式為什麼這樣寫,在新程式碼中,這樣的寫法不應該再用了。

以下介紹Event例項的屬性和方法。

bubbles,eventPhase

以下屬性與事件的階段有關。

(1)bubbles

bubbles屬性返回一個布林值,表示當前事件是否會冒泡。該屬性為只讀屬性,只能在新建事件時改變。除非顯式宣告,Event建構函式生成的事件,預設是不冒泡的。

function goInput
(e) { if (!e.bubbles) { passItOn(e); } else { doOutput(e); } }

上面程式碼根據事件是否冒泡,呼叫不同的函式。

(2)eventPhase

eventPhase屬性返回一個整數值,表示事件目前所處的節點。

var phase = event.eventPhase;
  • 0,事件目前沒有發生。
  • 1,事件目前處於捕獲階段,即處於從祖先節點向目標節點的傳播過程中。該過程是從Window物件到Document節點,再到HTMLHtmlElement節點,直到目標節點的父節點為止。
  • 2,事件到達目標節點,即target屬性指向的那個節點。
  • 3,事件處於冒泡階段,即處於從目標節點向祖先節點的反向傳播過程中。該過程是從父節點一直到Window物件。只有bubbles屬性為true時,這個階段才可能發生。

cancelable,defaultPrevented

以下屬性與事件的預設行為有關。

(1)cancelable

cancelable屬性返回一個布林值,表示事件是否可以取消。該屬性為只讀屬性,只能在新建事件時改變。除非顯式宣告,Event建構函式生成的事件,預設是不可以取消的。

var bool = event.cancelable;

如果要取消某個事件,需要在這個事件上面呼叫preventDefault方法,這會阻止瀏覽器對某種事件部署的預設行為。

(2)defaultPrevented

defaultPrevented屬性返回一個布林值,表示該事件是否呼叫過preventDefault方法。

if (e.defaultPrevented) {
  // ...
}

currentTarget,target

以下屬性與事件的目標節點有關。

(1)currentTarget

currentTarget屬性返回事件當前所在的節點,即正在執行的監聽函式所繫結的那個節點。作為比較,target屬性返回事件發生的節點。如果監聽函式在捕獲階段和冒泡階段觸發,那麼這兩個屬性返回的值是不一樣的。

function hide(e){
  console.log(this === e.currentTarget);  // true
  e.currentTarget.style.visibility = "hidden";
}

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

上面程式碼中,點選para節點,該節點會不可見。另外,在監聽函式中,currentTarget屬性實際上等同於this物件。

(2)target

target屬性返回觸發事件的那個節點,即事件最初發生的節點。如果監聽函式不在該節點觸發,那麼它與currentTarget屬性返回的值是不一樣的。

function hide(e){
  console.log(this === e.target);  // 有可能不是true
  e.target.style.visibility = "hidden";
}

// HTML程式碼為
// <p id="para">Hello <em>World</em></p>
para.addEventListener('click', hide, false);

上面程式碼中,如果在para節點的em子節點上面點選,則e.target指向em子節點,導致em子節點(即World部分)會不可見,且輸出false。

在IE6—IE8之中,該屬性的名字不是target,而是srcElement,因此經常可以看到下面這樣的程式碼。

function hide(e) {
  var target = e.target || e.srcElement;
  target.style.visibility = 'hidden';
}

type,detail,timeStamp,isTrusted

以下屬性與事件物件的其他資訊相關。

(1)type

type屬性返回一個字串,表示事件型別,大小寫敏感。

var string = event.type;

(2)detail

detail屬性返回一個數值,表示事件的某種資訊。具體含義與事件型別有關,對於滑鼠事件,表示滑鼠按鍵在某個位置按下的次數,比如對於dblclick事件,detail屬性的值總是2。

function giveDetails(e) {
  this.textContent = e.detail;
}

el.onclick = giveDetails;

(3)timeStamp

timeStamp屬性返回一個毫秒時間戳,表示事件發生的時間。

var number = event.timeStamp;

Chrome在49版以前,這個屬性返回的是一個整數,單位是毫秒(millisecond),表示從Unix紀元開始的時間戳。從49版開始,該屬性返回的是一個高精度時間戳,也就是說,毫秒之後還帶三位小數,精確到微秒。並且,這個值不再從Unix紀元開始計算,而是從PerformanceTiming.navigationStart開始計算,即表示距離使用者導航至該網頁的時間。如果想將這個值轉為Unix紀元時間戳,就要計算event.timeStamp + performance.timing.navigationStart

下面是一個計算滑鼠移動速度的例子,顯示每秒移動的畫素數量。

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;
});

(4)isTrusted

isTrusted屬性返回一個布林值,表示該事件是否為真實使用者觸發。

var bool = event.isTrusted;

使用者觸發的事件返回true,指令碼觸發的事件返回false

preventDefault()

preventDefault方法取消瀏覽器對當前事件的預設行為,比如點選連結後,瀏覽器跳轉到指定頁面,或者按一下空格鍵,頁面向下滾動一段距離。該方法生效的前提是,事件的cancelable屬性為true,如果為false,則呼叫該方法沒有任何效果。

該方法不會阻止事件的進一步傳播(stopPropagation方法可用於這個目的)。只要在事件的傳播過程中(捕獲階段、目標階段、冒泡階段皆可),使用了preventDefault方法,該事件的預設方法就不會執行。

// HTML程式碼為
// <input type="checkbox" id="my-checkbox" />

var cb = document.getElementById('my-checkbox');

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

上面程式碼為點選單選框的事件,設定監聽函式,取消預設行為。由於瀏覽器的預設行為是選中單選框,所以這段程式碼會導致無法選中單選框。

利用這個方法,可以為文字輸入框設定校驗條件。如果使用者的輸入不符合條件,就無法將字元輸入文字框。

function checkName(e) {
  if (e.charCode < 97 || e.charCode > 122) {
    e.preventDefault();
  }
}

上面函式設為文字框的keypress監聽函式後,將只能輸入小寫字母,否則輸入事件的預設事件(寫入文字框)將被取消。

如果監聽函式最後返回布林值false(即return false),瀏覽器也不會觸發預設行為,與preventDefault方法有等同效果。

stopPropagation()

stopPropagation方法阻止事件在DOM中繼續傳播,防止再觸發定義在別的節點上的監聽函式,但是不包括在當前節點上新定義的事件監聽函式。

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

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

將上面函式指定為監聽函式,會阻止事件進一步冒泡到el節點的父節點。

stopImmediatePropagation()

stopImmediatePropagation方法阻止同一個事件的其他監聽函式被呼叫。

如果同一個節點對於同一個事件指定了多個監聽函式,這些函式會根據新增的順序依次呼叫。只要其中有一個監聽函式呼叫了stopImmediatePropagation方法,其他的監聽函式就不會再執行了。

function l1(e){
  e.stopImmediatePropagation();
}

function l2(e){
  console.log('hello world');
}

el.addEventListener('click', l1, false);
el.addEventListener('click', l2, false);

上面程式碼在el節點上,為click事件添加了兩個監聽函式l1和l2。由於l1呼叫了stopImmediatePropagation方法,所以l2不會被呼叫。