JavaScript 事件流、事件處理程序及事件對象總結(jié)
JS與HTML之間的交互通過事件實現(xiàn)。事件就是文檔或瀏覽器窗口中發(fā)生的一些特定的交互瞬間??梢允褂?或處理程序)來預(yù)定事件,以便事件發(fā)生時執(zhí)行相應(yīng)的代碼。這種在傳統(tǒng)軟件工程中被稱為觀察員模式,支持頁面的行為與頁面的外觀之間的松散耦合。本文將介紹JS事件相關(guān)的基礎(chǔ)知識。
一、事件流
事件流描述的是從頁面中接受事件的順序。
事件冒泡
事件開始時由最具體的元素(文檔中嵌套層次最深的那個節(jié)點)接收,然后逐級向上傳播到較為不具體的結(jié)點(文檔)。以下面HTML頁面為例,如果你點擊了頁面中的按鈕,那么”click”事件會按照< button>、< body>、< html>、document的順序傳播。換句話說,事件冒泡指的就是事件從底層觸發(fā)事件的元素開始沿著DOM樹向上傳播,直到document對象。
<html> <head> <title>Test</title> </head> <body> <button id="myBtn">A Btn</button> </body> </html>
事件捕獲
與事件冒泡的思路相反,事件捕獲的思想是不太具體的節(jié)點應(yīng)該更早地接收到事件,最具體的結(jié)點應(yīng)該最后才接收事件。同樣還是上面那個例子,點擊頁面中的按鈕之后,”click”事件會按照document、< html>、< body>、< button>的順序傳播。換句話說,事件捕獲就是指事件從document對象開始沿著DOM樹向下傳播,直到事件的實際目標元素。
DOM事件流
“DOM2級事件”規(guī)定的事件包括三個階段: 事件捕獲階段、處于目標階段和事件冒泡階段。首先發(fā)生的是事件捕獲,為截獲事件提供了機會。然后是實際的目標接收到事件。最后一個階段是冒泡階段,可以在這個階段對事件做出響應(yīng)。
還是以之前的點擊按鈕為例,在DOM事件流中,捕獲階段,”click”事件從document開始向下傳遞到body元素(注意,實際目標button在捕獲階段不會接收到事件)。目標階段,button元素接收到”click”事件。最后,冒泡階段,事件又被傳播回文檔。
二、事件處理程序
事件是用戶或瀏覽器自身執(zhí)行的某種動作,而響應(yīng)某個事件的函數(shù)就叫做事件處理程序或事件。
HTML事件處理程序
這里的HTML事件處理程序指的是直接在HTML元素里面通過特性(attribute)定義的事件處理程序,請看下面的代碼示例。這樣是定的事件處理程序會創(chuàng)建一個封裝著元素屬性值的函數(shù),this值等于事件的目標元素。通過這種方法指定事件處理程序存在不少缺點,不推薦使用。
<button onclick="alert('HaHa~')">Btn-1</button> <button onclick="alert('event.type')">Btn-2</button> <button onclick="handler()">Btn-3</button> <script type="text/javascript"> function handler() { alert("Haha~"); } </script>
DOM0級事件處理程序
通過JS指定事件處理程序的傳統(tǒng)方式就是將一個函數(shù)賦值給一個事件處理程序?qū)傩?,請看下面代碼示例。通過這種方式指定的事件處理程序是在元素的作用域中運行,this引用的是當(dāng)前元素。這種方式添加的事件處理程序會在事件流的冒泡階段被處理。若要刪除事件,直接令onclick的值為空即可。
var btn = document.getElementById("myBtn"); btn.onclick = function() { console.log("this.id"); // "myBtn" }; // 刪除事件處理程序 btn.onclick = null;
DOM2級事件處理程序
“DOM2級事件”定義了兩個方法用于指定和刪除事件處理程序,addEventListener()和removeEventListener()。所有DOM節(jié)點中都包含這兩個方法。這兩個方法都接收3個參數(shù),要處理的事件、處理函數(shù)、布爾值。最后的布爾值為true時表示在捕獲階段調(diào)用事件處理程序,為false時表示在冒泡階段調(diào)用處理程序。與DOM0級方法一樣,這里添加的事件處理程序也是在其依附的元素的作用域中運行。DOM2級方法添加事件處理程序的優(yōu)勢是可以添加多個事件處理程序。這些事件處理程序會按照它們被添加的順序觸發(fā)。下面是代碼示例:
var btn = document.getElementById("myBtn"); // 添加,觸發(fā)點擊事件時先輸出"myBtn"再輸出"HaHa~" btn.addEventListener("click", function() { console.log(this.id); }, false); btn.addEventListener("click", function() { console.log("HaHa~"); }, false);
通過addEventListener()添加的事件只能通過removeEventListener()來刪除。刪除時傳入的參數(shù)與添加時使用的參數(shù)應(yīng)該保持一致。這也意味著通過addEventListener()添加的匿名函數(shù)將無法刪除,因為無法將添加時傳遞的匿名函數(shù)傳給removeEventListener(),即便在刪除的時候?qū)懥艘粋€一模一樣的函數(shù),但此時這個函數(shù)只是一個新的匿名函數(shù)。請看下面代碼示例:
var btn = document.getElementById("myBtn"); // 無法刪除匿名函數(shù) 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);
大多數(shù)情況下,都是將事件處理程序添加到事件流的冒泡階段,這樣可以最大限度地兼容各種瀏覽器。最好只在需要在事件到達目標之前截獲它的時候才將事件處理程序添加到捕獲階段。JS高級程序設(shè)計上給出的建議是,如果不是特別需要,不建議在事件捕獲階段注冊事件處理程序。
IE事件處理程序
IE實現(xiàn)了與DOM中類似的兩個方法: attachEvent()和deleteEvent()。這兩個方法接收兩個參數(shù),事件處理程序名稱和事件處理程序。注意,第一個參數(shù)是事件處理程序名稱而不是事件名稱,也就是說在注冊點擊事件的處理程序時應(yīng)該傳入”onclick”而不是”click”,這里跟DOM的方法有些差別。另外,這兩個方法注冊的事件處理程序是在全局作用域中運行而不是元素作用域,this的值指向window。還有一點需要特別小心,通過attachEvent()方法也可以添加多個事件處理程序,但是它們的執(zhí)行順序卻不是按照它們被添加的順序,而是完全相反,跟DOM方法截然不同。下面是代碼示例:
var btn = document.getElementById("myBtn"); function handler1() { // ... } function handler2() { // ... } // 添加,觸發(fā)點擊事件時先執(zhí)行handler2再執(zhí)行handler1 btn.attachEvent("onclick", handler1); btn.attachEvent("onclick", handler2); // 刪除 btn.deleteEvent("onclick", handler1); btn.deleteEvent("onclick", handler2);
三、事件對象
在觸發(fā)DOM上的某個事件時,會產(chǎn)生一個事件對象event,這個對象中包含所有與事件有關(guān)的信息,包括導(dǎo)致事件的元素、事件的類型以及其他與特定事件相關(guān)的信息。
DOM中的事件對象
兼容DOM的瀏覽器會將一個event對象傳入事件處理程序中,無論指定事件處理程序時用的是DOM0還是DOM2的方法,都會傳入event對象。event對象只有在事件處理程序執(zhí)行期間才會存在,一旦事件處理程序執(zhí)行完畢,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對象包含與創(chuàng)建它的特定事件有關(guān)的屬性和方法,觸發(fā)的事件類型不一樣,可用的屬性方法也有所不同。但是所有的事件都會有下列的屬性或方法:
- bubbles: 布爾值,表示事件是否冒泡
- cancelable: 布爾值,表示是否可以取消事件的默認行為
- currentTarget: 元素,事件處理程序當(dāng)前正在處理事件的那個元素
- defaultPrevented: 布爾值,表示是否調(diào)用過preventDefault()方法
- detail: 整數(shù),與事件相關(guān)的細節(jié)信息
- eventPhase: 整數(shù),調(diào)用事件處理程序的階段,1表示捕獲階段,2表示目標階段,3表示冒泡階段
- preventDefault(): 函數(shù),取消事件的默認行為,cancelable為true時可以調(diào)用該方法
- stopImmediatePropagation(): 函數(shù),取消事件的進一步捕獲或冒泡,同時阻止任何事件處理程序被調(diào)用
- stopPropagation(): 函數(shù),取消事件的進一步捕獲或冒泡,bubbles為true時可以調(diào)用這個方法
- target: 元素,事件的目標
- trusted: 布爾值,為true時表示事件是瀏覽器生成的,否則表示事件是通過JS創(chuàng)建的
- type: 字符串,被觸發(fā)的事件類型
- view: 與事件關(guān)聯(lián)的抽象視圖,等同于發(fā)生事件的window對象
下面代碼示例展示了上述部分屬性的用法,也可以幫助我們進一步理解事件流。假設(shè)頁面中有一個按鈕”myBtn”。當(dāng)點擊按鈕時,this和currentTarget都等于body元素,因為事件處理程序是注冊在body元素上。target的值卻等于按鈕元素,因為它是click事件的真正目標。由于按鈕上沒有注冊事件處理程序,結(jié)果”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()方法取消了事件的進一步捕獲或冒泡。當(dāng)我點擊按鈕時,本來應(yīng)該會因為事件冒泡機制觸發(fā)按鈕和body元素上的點擊事件處理程序,輸出”From Bth …”和”From Body …”?,F(xiàn)在點擊事件在按鈕元素上觸發(fā)之后就被阻止繼續(xù)在DOM層次中的傳播,因此body上的事件處理程序不會被觸發(fā)。
var btn = document.getElementById("myBtn"); btn.onclick = function(event) { console.log("From Bth ..."); event.stopPropagation(); // 停止事件傳播 }; document.body.onclick = function() { console.log("From Body ..."); };
IE中的事件對象
在IE中,使用DOM0的方法添加事件處理程序時,event對象作為window對象的一個屬性存在。如果是通過attachEvent()方法添加,則event對象是作為參數(shù)傳入事件處理函數(shù)。下面是代碼示例:
var btn = document.getElementById("myBtn"); btn.onclick = function() { var event = window.event; console.log(event.type); // "click" }; btn.attachEvent("onclick", function(event) { console.log(event.type); // "click" });
IE的event對象同樣也包含與創(chuàng)建它的事件相關(guān)的屬性和方法,這些屬性和方法也會因為事件類型的不同而有所差異。但所有事件對象都會包含下列屬性:
- cancelBubble: 布爾值,可讀可寫,默認為false。將其設(shè)置為true時取消事件冒泡
- returnValue: 布爾值,可讀可寫,默認為true。將其設(shè)置為false時取消事件的默認行為
- srcElment: 元素,事件的目標元素,與DOM中的target屬性相同
- type: 字符串,事件類型
在IE中,事件處理程序的作用域是根據(jù)指定它的方式來確定,this的值不一定是指向事件的目標元素。因此,使用srcElement屬性更具保險。請看下面代碼實例,第一種方式中this的值為目標元素,而第二種方式,前面講過這種方式的事件處理程序是在全局作用域中執(zhí)行,因此this的值為window。
var btn = document.getElementById("myBtn"); btn.onclick = function() { console.log(window.event.srcElement === this); // true } btn.attachEvent("onclick", function(event) { console.log(event.srcElement === this); // false });
以上就是本文的全部內(nèi)容,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,同時也希望多多支持腳本之家!
相關(guān)文章
JavaScript實現(xiàn)數(shù)據(jù)可視化圖表的示例代碼
這篇文章主要介紹了如何使用JavaScript創(chuàng)建實時數(shù)據(jù)可視化圖表,我們將使用流行的圖表庫,如Chart.js,來展示如何將實時數(shù)據(jù)動態(tài)呈現(xiàn)在圖表中,感興趣的可以了解下2024-01-01javascript 驗證碼生成代碼 推薦學(xué)習(xí)
非常不錯的用javascript實現(xiàn)的驗證碼實現(xiàn)代碼。2009-07-07js關(guān)閉瀏覽器窗口及檢查瀏覽器關(guān)閉事件
js關(guān)閉瀏覽器窗口,不彈出提示框。支持ie6+,火狐,谷歌等瀏覽器,下面以一個示例為大家詳細介紹下具體的實現(xiàn)方法,感興趣的朋友可以參考下2013-09-09getElementsByTagName vs selectNodes效率 及兼容的selectNodes實現(xiàn)
天在csdn上看到有人問 getElementsByTagName 和 selectNodes誰更快 ,這個還真沒研究過。2010-02-02JavaScript驗證18位身份證號碼最后一位正確性的實現(xiàn)代碼
這篇文章主要介紹了JavaScript驗證18位身份證號碼最后一位正確性的實現(xiàn)代碼,小編親測有效,需要的朋友可以參考下2014-08-08JS typeof fn === ''function'' && fn()詳解
最近在學(xué)習(xí)js的時候發(fā)現(xiàn)很多人都喜歡在JS 中存在fn && fn() 執(zhí)行語句或者typeof fn === 'function' && fn(),下面這篇文章就為大家分享一下2020-08-08