JavaScript事件流、事件處理程序及事件對象總結

字號+ 編輯: Snake 修訂: 科学鼠辈 來源: 兴趣部落 2023-09-12 我要說兩句(0)

JS與HTML之間的交互通過事件實現。事件就是文档或瀏覽器窗口中發生的一些特定的交互瞬間。可以使用監聽器(或處理程序)來預定事件,以便事件發生時執行相應的代碼。這種在傳統軟體工程中被稱爲觀察員模式,支持頁面的行爲與頁面的外觀之間的松散耦合。本文將介紹JS事件相關的基礎知識。

一、事件流 

事件流描述的是從頁面中接受事件的順序。 

事件冒泡 

事件開始時由最具體的元素(文档中嵌套層次最深的那個節點)接收,然後逐級向上傳播到較爲不具體的結點(文档)。以下面HTML頁面爲例,如果你點擊了頁面中的按鈕,那麽”click”事件會按照< button>、< body>、< html>、document的順序傳播。換句話說,事件冒泡指的就是事件從底層觸發事件的元素開始沿著DOM樹向上傳播,直到document對象。 

<html>
 <head>
  <title>Test</title>
 </head>
 <body>
  <button id="myBtn">A Btn</button>
 </body>
</html>

事件捕獲 

與事件冒泡的思路相反,事件捕獲的思想是不太具體的節點應該更早地接收到事件,最具體的結點應該最後才接收事件。同樣還是上面那個例子,點擊頁面中的按鈕之後,”click”事件會按照document、< html>、< body>、< button>的順序傳播。換句話說,事件捕獲就是指事件從document對象開始沿著DOM樹向下傳播,直到事件的實際目標元素。 

DOM事件流 

“DOM2級事件”槼定的事件包括三個階段: 事件捕獲階段、處於目標階段和事件冒泡階段。首先發生的是事件捕獲,爲截獲事件提供了機會。然後是實際的目標接收到事件。最後一個階段是冒泡階段,可以在這個階段對事件做出響應。 

還是以之前的點擊按鈕爲例,在DOM事件流中,捕獲階段,”click”事件從document開始向下傳遞到body元素(注意,實際目標button在捕獲階段不會接收到事件)。目標階段,button元素接收到”click”事件。最後,冒泡階段,事件又被傳播回文档。 

二、事件處理程序 

事件是用戶或瀏覽器自身執行的某種動作,而響應某個事件的函數就叫做事件處理程序或事件偵聽器。 

HTML事件處理程序 

這裡的HTML事件處理程序指的是直接在HTML元素裡面通過特性(attribute)定義的事件處理程序,請看下面的代碼示例。這樣是定的事件處理程序會創建一個封裝著元素屬性值的函數,this值等於事件的目標元素。通過這種方法指定事件處理程序存在不少缺點,不推薦使用。 

<button onclick="alert('HaHa~')">Btn-1</button>
<button onclick="alert('event.type')">Btn-2</button>
<button onclick="handler()">Btn-3</button>

DOM0級事件處理程序 

通過JS指定事件處理程序的傳統方式就是將一個函數賦值給一個事件處理程序屬性,請看下面代碼示例。通過這種方式指定的事件處理程序是在元素的作用域中運行,this引用的是當前元素。這種方式添加的事件處理程序會在事件流的冒泡階段被處理。若要刪除事件,直接令onclick的值爲空即可。 

var btn = document.getElementById("myBtn");
btn.onclick = function() {
 console.log("this.id"); // "myBtn"
};
// 刪除事件處理程序
btn.onclick = null;

DOM2級事件處理程序 

“DOM2級事件”定義了兩個方法用於指定和刪除事件處理程序,addEventListener()和removeEventListener()。所有DOM節點中都包含這兩個方法。這兩個方法都接收3個參數,要處理的事件、處理函數、布爾值。最後的布爾值爲true時表示在捕獲階段調用事件處理程序,爲false時表示在冒泡階段調用處理程序。與DOM0級方法一樣,這裡添加的事件處理程序也是在其依附的元素的作用域中運行。DOM2級方法添加事件處理程序的優勢是可以添加多個事件處理程序。這些事件處理程序會按照它們被添加的順序觸發。下面是代碼示例: 

var btn = document.getElementById("myBtn");
// 添加,觸發點擊事件時先輸出"myBtn"再輸出"HaHa~"
btn.addEventListener("click", function() {
 console.log(this.id);
}, false);
btn.addEventListener("click", function() {
 console.log("HaHa~");
}, false);

通過addEventListener()添加的事件只能通過removeEventListener()來刪除。刪除時傳入的參數與添加時使用的參數應該保持一致。這也意味著通過addEventListener()添加的匿名函數將無法刪除,因爲無法將添加時傳遞的匿名函數傳給removeEventListener(),即便在刪除的時候寫了一個一模一樣的函數,但此時這個函數隻是一個新的匿名函數。請看下面代碼示例: 

var btn = document.getElementById("myBtn");
// 無法刪除匿名函數
btn.addEventListener("click", function() {
 console.log(this.id);
}, false);
btn.removeEventListener("click", function() {
 console.log(this.id);
}, false);
// 正確的添加和刪除方式
function handler() {
 console.log(this.id);
}
btn.addEventListener("click", handler, false);
btn.removeEventListener("click", handler, false);

大多數情況下,都是將事件處理程序添加到事件流的冒泡階段,這樣可以最大限度地兼容各種瀏覽器。最好只在需要在事件到達目標之前截獲它的時候才將事件處理程序添加到捕獲階段。JS高級程序設計上給出的建議是,如果不是特別需要,不建議在事件捕獲階段注冊事件處理程序。 

IE事件處理程序 

IE實現了與DOM中類似的兩個方法: attachEvent()和deleteEvent()。這兩個方法接收兩個參數,事件處理程序名稱和事件處理程序。注意,第一個參數是事件處理程序名稱而不是事件名稱,也就是說在注冊點擊事件的處理程序時應該傳入”onclick”而不是”click”,這裡跟DOM的方法有些差別。另外,這兩個方法注冊的事件處理程序是在全局作用域中運行而不是元素作用域,this的值指向window。還有一點需要特別小心,通過attachEvent()方法也可以添加多個事件處理程序,但是它們的執行順序卻不是按照它們被添加的順序,而是完全相反,跟DOM方法截然不同。突然覺得IE真的特別反人類~~~下面是代碼示例: 

var btn = document.getElementById("myBtn");
function handler1() { // ... }
function handler2() { // ... }
// 添加,觸發點擊事件時先執行handler2再執行handler1
btn.attachEvent("onclick", handler1);
btn.attachEvent("onclick", handler2);
// 刪除
btn.deleteEvent("onclick", handler1);
btn.deleteEvent("onclick", handler2);

三、事件對象 

在觸發DOM上的某個事件時,會産生一個事件對象event,這個對象中包含著所有與事件有關的信息,包括導致事件的元素、事件的類型以及其他與特定事件相關的信息。 

DOM中的事件對象 

兼容DOM的瀏覽器會將一個event對象傳入事件處理程序中,無論指定事件處理程序時用的是DOM0還是DOM2的方法,都會傳入event對象。event對象只有在事件處理程序執行期間才會存在,一旦事件處理程序執行完畢,event對象就會被銷毀。下面是代碼示例: 

var btn = document.getElementById("myBtn");
btn.onclick = function(event) {
 console.log(event.type); // "click"
}
btn.addEventListener("click", function(event) {
 console.log(event.type);
}, false);

event對象包含與創建它的特定事件有關的屬性和方法,觸發的事件類型不一樣,可用的屬性方法也有所不同。但是所有的事件都會有下列的屬性或方法: 

bubbles: 布爾值,表示事件是否冒泡 

cancelable: 布爾值,表示是否可以取消事件的默認行爲 

currentTarget: 元素,事件處理程序當前正在處理事件的那個元素 

defaultPrevented: 布爾值,表示是否調用過preventDefault()方法 

detail: 整數,與事件相關的細節信息 

eventPhase: 整數,調用事件處理程序的階段,1表示捕獲階段,2表示目標階段,3表示冒泡階段 

preventDefault(): 函數,取消事件的默認行爲,cancelable爲true時可以調用該方法 

stopImmediatePropagation(): 函數,取消事件的進一步捕獲或冒泡,同時阻止任何事件處理程序被調用 

stopPropagation(): 函數,取消事件的進一步捕獲或冒泡,bubbles爲true時可以調用這個方法 

target: 元素,事件的目標 

trusted: 布爾值,爲true時表示事件是瀏覽器生成的,否則表示事件是通過JS創建的 

type: 字符串,被觸發的事件類型 

view: 與事件關聯的抽象視圖,等同於發生事件的window對象 

下面代碼示例展示了上述部分屬性的用法,也可以幫助我們進一步理解事件流。假設頁面中有一個按鈕”myBtn”。當點擊按鈕時,this和currentTarget都等於body元素,因爲事件處理程序是注冊在body元素上。target的值卻等於按鈕元素,因爲它是click事件的真正目標。由於按鈕上沒有注冊事件處理程序,結果”click”事件冒泡到了document.body那裡才得到處理。 

document.body.onclick = function(event) {
 console.log(event.currentTarget === document.body); // true
 console.log(this === document.body); // true
 console.log(event.target === document.getElementById("myBtn")); // true
};

再看一個例子,下面代碼中,stopPropagation()方法取消了事件的進一步捕獲或冒泡。當我點擊按鈕時,本來應該會因爲事件冒泡機制觸發按鈕和body元素上的點擊事件處理程序,輸出”From Bth …”和”From Body …”。現在點擊事件在按鈕元素上觸發之後就被阻止繼續在DOM層次中的傳播,因此body上的事件處理程序不會被觸發。 

var btn = document.getElementById("myBtn");
btn.onclick = function(event) {
 console.log("From Bth ...");
 event.stopPropagation(); // 停止事件傳播
};
document.body.onclick = function() {
 console.log("From Body ...");
};
閲完此文,您的感想如何?
  • 有用

    0

  • 沒用

    0

  • 開心

    0

  • 憤怒

    0

  • 可憐

    0

1.如文章侵犯了您的版權,請發郵件通知本站,該文章將在24小時内刪除;
2.本站標注原創的文章,轉發時煩請注明來源;
3.交流群: 2702237 13835667

相關課文
  • JS如何防止父節點的事件運行

  • nodejs編寫一個簡單的http請求客戶耑代碼demo

  • 說一則爲什麽後耑開發人員不選擇node.js的原因

  • 使用Sublime Text3 開發React-Native的配置

我要說說
網上賓友點評