JavaScript與HTML之間的交互是經過事件實現的。事件,就是文檔或瀏覽器窗口中發生的一些特定的交互瞬間,經過監聽特定事件的發生,你能響應相關的操做。圖片引用:UI Eventshtml
主要是當時的IE團隊提出的事件流逝是事件冒泡流,而Netscape提出的是事件捕獲流,
可使用DOM2級定義的addEventListener()方法來處理在冒泡或者捕獲階段調用事件處理程序。瀏覽器
即事件開始時由最具體的元素(文檔中嵌套最深的節點)接收,而後逐級向上傳播到較爲不具體的節點。
閉包
即最外層的元素更早接收到事件,而最具體的元素應該最後接收到事件。事件捕獲的用意在於在事件到達預約目標以前捕獲它。
dom
而「DOM2級事件」規定的事件流包括三個階段:事件捕獲階段,處於目標階段和事件冒泡階段。首先發生的是事件捕獲,而後是實際的目標接收到事件,最後一個階段是冒泡階段。
函數
addEventListener函數接收三個參數,要處理的事件名、做爲事件處理程序的函數和一個布爾值,最後的布爾值若是是true,表示在不回階段調用事件處理程序;若是是false,表示在冒泡階段調用事件處理程序。測試
事件捕獲:this
//html <!DOCTYPE html> <html> <head> <title></title> </head> <body> <div class="a"> <div class="b"> <button class="c">click</button> </div> </div> </body> </html> var $body = document.querySelector("body"), $a = document.querySelector(".a"), $b = document.querySelector(".b"), $c = document.querySelector(".c"); $body.addEventListener("click",function(){ console.log(this); },true); $a.addEventListener("click",function(){ console.log(this); },true); $b.addEventListener("click",function(){ console.log(this); },true); $c.addEventListener("click",function(){ console.log(this); },true);
單擊button,則
代理
事件冒泡:code
$body.addEventListener("click",function(){ console.log(this); },false); $a.addEventListener("click",function(){ console.log(this); },false); $b.addEventListener("click",function(){ console.log(this); },false); $c.addEventListener("click",function(){ console.log(this); },false);
單擊button,則
htm
上述代碼在IE9和其餘現代瀏覽器都支持(測試頁)。IE8及更早版本不支持DOM事件流只有事件冒泡。因此咱們有兩個階段能夠在目標對象上操做事件,但爲了兼容性考慮,通常用冒泡的狀況比較多。
事件是用戶或瀏覽器自身執行的某種動做,而響應某個事件的函數就叫作事件處理程序(或事件偵聽器)。
即內聯式處理函數,以"on"開頭:
<button onclick="alert(this)"></button>
雖然方便,但有很大問題,首先,存在時差問題,由於用戶可能會在HTML元素一出如今頁面上就觸發相應的事件,但當時的事件處理程序有可能尚不具有執行條件;另外一個缺點是:這樣擴展事件處理程序的做用域鏈在不一樣瀏覽器會致使不一樣結果;而最後的缺點則是HTML與JavaScript代碼緊密耦合,若是要更換事件處理程序,就要改動兩個地方。
經過JavaScript指定事件處理程序的傳統方式,就是將一個函數賦值給一個事件處理程序屬性。
每一個元素(包括window和docuemnt)都有本身的事件處理程序屬性,這些屬性一般所有小寫:
var btn = document.getElementById('myBtn'); btn.onclick = function(){ alert("Clicked"); }
以這種方式添加的事件處理程序會在事件流的冒泡階段被處理。
「DOM2級事件」定義了兩個方法,用於處理指定和刪除事件處理程序的操做:addEventListener()和removeEventListener()。全部DOM節點中都包含這兩個方法,在上面的事件捕獲和冒泡有用到。使用DOM2級方法添加事件處理程序的主要好處是能夠添加多個事件處理程序。
IE實現的方法是:attachEvent()和detachEvent()。這兩個方法接收相同的兩個參數:事件處理程序名稱與事件處理程序函數。經過attachEvent()添加的事件處理程序都會被添加到冒泡階段:
var btn = document.getElementById("myBtn"); btn.attachEvent("onclick",function(){ alert("Clicked"); })
這裏的參數中表行爲的是onclick而非DOM的addEventListener()方法中的click,在IE中使用attachEvent()與使用DOM0級方法的主要區別在於事件處理程序的做用域,在使用DOM0級方法的狀況下,事件處理程序會在其所屬元素的做用域內運行;在使用attachEvent()方法的狀況下,事件處理程序會在全局做用域中運行,所以this等於window
var btn = document.getElementById("myBtn"); btn.attachEvent('onclick',function(){ alert(this === window);// true });
在觸發DOM上的某個事件時,會產生一個事件對象event,全部瀏覽器都支持event對象,但支持方式不一樣。
event對象包含與建立它的特定事件有關的屬性和方法。觸發的事件類型不同,可用的屬性和方法也不同。不過,全部事件都有下表的成員屬性。
屬性/方法 | 類型 | 讀/寫 | 說明 |
---|---|---|---|
bubbles | boolean | 只讀 | 代表事件是否冒泡 |
cancelable | boolean | 只讀 | 代表是否能夠取消事件的默認行爲 |
currentTarget | element | 只讀 | 其事件處理程序當前正在處理事件的那個元素 |
defaultPrevented | boolean | 只讀 | 爲true表示已經調用了preventDefault()DOM3新增 |
datail | integer | 只讀 | 與事件相關的細節信息 |
eventPhase | integer | 只讀 | 調用事件處理程序的階段:1表示捕獲階段,2表示處於目標 3表示冒泡階段 |
preventDefault() | function | 只讀 | 取消事件的默認行爲。若是cancelable是true,則可使用這個方法 |
stopImmediatePropagation() | function | 只讀 | 取消事件的進一步捕獲或冒泡,同時阻止任何事件處理程序被調用(DOM3級新增) |
stopPropagation() | function | 只讀 | 取消事件的進一步捕獲或冒泡,若是bubbles爲true,則可使用這個方法 |
target | element | 只讀 | 事件的目標 |
isTrusted | boolean | 只讀 | 返回一個布爾值,代表當前事件是不是由用戶行爲觸發,仍是由一個腳本生成的 |
type | string | 只讀 | 被觸發的事件的類型 |
view | AbstractView | 只讀 | 與事件關聯的抽象視圖。等同於發生事件的window對象 |
更詳細的文檔能夠看 這裏
在事件處理程序內部,對象this始終等於currentTarget的值,而target則只包含事件的實際目標。若是直接將事件處理程序指定給了目標元素,則this,currentTarget和target包含相同的值。
根據event所擁有的屬性,就能夠更加細緻的操做事件。在w3c的這篇Events中,咱們能夠知道哪些事件能夠冒泡.
首先是冒泡事件,若是隻須要在具體節點觸發,而不冒泡到包裹層中,則可使用與冒泡行爲相關的一系列屬性,舉例:
<div class="a"> <a href="http://www.cnblogs.com"></a> </div>
在這個div中,若是div和a都有監聽點擊事件,那麼在點擊a標籤的時候(默認事件流在已過捕獲階段),會先觸發a的點擊事件,而後是div的點擊事件,最後是a標籤的默認事件。若是不想冒泡,也就是說點擊a時只觸發a的點擊事件,則可使用stopPropagation()方法:
var $a = document.querySelector("a"); $a.addEventListener("click",function(event){ if(event.bubbles){ event.stopPropagation(); } });
也能夠在處理程序裏直接刪除節點,也能阻止冒泡。
固然,有時咱們不但願跳轉頁面,而是執行自定義的腳本,那就可使用preventDefault()方法:
var $a = document.querySelector("a"); $a.addEventListener("click",function(event){ if(event.bubbles){ event.preventDefault(); // do something } });
有時咱們可能會接手他人的代碼,在不瞭解具體代碼的狀況下,若是隻是單個獨立事件須要修改,則能夠在運行老的js代碼以前,使用stopImmediatePropagation()方法。
$a.addEventListener('click',function(event){ event.preventDefault(); event.stopImmediatePropagation(); // do something }) $a.addEventListener("click",function(event){ alert("這是a標籤"); });
固然,冒泡並非說是一件壞事,由於場景不少變,在某些場景下,冒泡也是頗有用的,好比說事件代理(delegation)。
有時咱們須要對生成的節點附加操做,event老是須要先找到element,再進行註冊,才能夠進行事件監聽,那麼對於這些生成的節點來講,固然能夠在生成以後再次對該節點從新註冊事件。但若是是生成了同一個標記的節點呢?
那麼咱們不只須要把以前註冊的事件remove,還須要從新綁定以前的標記,不然會二次觸發。這時候事件代理
就能夠解決這一類問題。根據DOM事件流,咱們能夠知道,在節點冒泡時,能夠一直往上冒泡,因此,咱們徹底能夠把事件代理給點擊節點的父節點來作,根據event.target屬性,咱們能夠知道指向觸發事件的對象究竟是誰,也所以,不用再使用繁瑣的從新註冊事件,就能夠直接操做子元素。像jQuery,Zepto中都有關於事件代理的處理方法,後篇會繼續涉及到。
$div.addEventListener("click",function(event){ alert("這是外層"); if(event.target) { event.target.style.visibility = 'hidden'; } });
冒泡也會有反作用,mouseout事件會致使離開內部節點時提早觸發,爲此,jQuery自定義了mouseenter和mouseleave事件,Zepto也有相應的事件。
只有在事件處理程序執行期間,event對象纔會存在;一旦事件處理程序執行完成,event對象就會被銷燬(閉包)
在IE中訪問event,特別是IE6~8尤爲注意,在使用DOM0級方法添加事件處理程序時,event對象做爲window對象的一個屬性存在。
var btn = document.getElementById('a'); btn.onclick = function(){ var event = window.event; alert(event.type); //"click" }
而IE中的event對象都會包含下表的屬性和方法:
屬性/方法 | 類型 | 讀/寫 | 說明 |
---|---|---|---|
cancelBubble | Boolean | 讀/寫 | 默認值爲false,但將其設置爲true就能夠取消事件冒泡(與DOM中的stopPropagation()方法的做用相同) |
returnValue | Boolean | 讀/寫 | 默認值爲false,但將其設置爲false就能夠取消事件的默認行爲(與DOM中的preventDefault()方法的做用相同) |
srcElement | Element | 只讀 | 事件的目標(與DOM中的target屬性相同) |
type | String | 只讀 | 被觸發的事件的類型 |
不表
結合jQuery和Zepto源碼總結。
部分用例:event