事件的本質是程序各個組成部分之間的一種通訊方式,也是異步編程的一種實現。DOM支持大量的事件,本章開始介紹DOM的事件編程。javascript
瀏覽器的事件模型,就是經過監聽函數(listener)對事件作出反應。事件發生後,瀏覽器監聽到了這個事件,就會執行對應的監聽函數。這是事件驅動編程模式(event-driven)的主要編程方式。html
DOM事件流(event flow)有兩種:事件冒泡、事件捕獲。不管是事件捕獲仍是事件冒泡,它們都有一個共同的行爲事件傳播。它就像一跟引線,只有經過引線才能將綁在引線上的鞭炮(事件監聽器)引爆。java
DOM標準事件流的觸發的前後順序爲:先捕獲再冒泡。即當觸發DOM事件時,會先進行事件捕獲,捕獲到事件源以後經過事件傳播進行事件冒泡。不一樣的瀏覽器對此有着不一樣的實現:IE10及如下不支持捕獲型事件,因此就少了一個事件捕獲階段,IE十一、Chrome 、Firefox、Safari等瀏覽器則同時存在。編程
若是但願事件到某個節點爲止,再也不向上或向下傳播,可使用事件對象的event.stopPropagation()
方法。它會阻止事件繼續捕獲或者冒泡。若是想要完全取消該事件,再也不觸發該節點所擁有的某個事件的全部監聽函數,可使用event.stopImmediatePropagation()
方法。瀏覽器
<body> <div id="parent"> 父元素 <div id="child"> 子元素 </div> </div> <script type="text/javascript"> // 經過"addEventListener"方法,採用事件冒泡方式給dom元素註冊click事件 var parent = document.getElementById("parent"); var child = document.getElementById("child"); document.body.addEventListener("click",function(e){ console.info("click-body"); },false); parent.addEventListener("click",function(e){ console.info("click-parent"); },false); child.addEventListener("click",function(e){ console.info("click-child"); },false); </script> </body>
事件捕獲(event capturing):順序是由外到內進行事件傳播,直到葉子節點。如點擊了子元素,若是父元素經過事件捕獲方式註冊了對應的事件的話,會先觸發父元素綁定的事件。dom
// 在原始代碼新增事件捕獲事件代碼 parent.addEventListener("click",function(e){ console.info("click-parent--事件捕獲"); },true); // 點擊子元素依次會輸出:click-parent--事件捕獲、click-child、click-parent、click-body
事件冒泡(dubbed bubbling):順序是由內到外進行事件傳播,直到根節點。異步
// 上述過程,點擊子元素依次會輸出:click-child、click-parent、click-body // 若是點擊子元素不想觸發父元素的事件怎麼辦 // 在原始代碼新增中止事件傳播--event.stopPropagation() child.addEventListener("click",function(e){ console.info("click-child"); e.stopPropagation(); },false); // 點擊子元素只會輸出:click-child
事件委託還有一個名字叫事件代理,通常用於動態生成的元素。事件委託是利用事件冒泡原理來實現的,只指定一個事件處理程序,就能夠管理某一類型的全部事件。異步編程
有三個同事預計會在週一收到快遞。爲簽收快遞有兩種辦法:一是三我的在公司門口等快遞;二是委託給前臺代爲簽收。現實當中大都採用委託的方案(公司也不會容忍那麼多員工站在門口就爲了等快遞)。前臺MM收到快遞後,她會判斷收件人是誰,而後按照收件人的要求籤收,甚至代爲付款。這種方案還有一個優點,那就是即便公司裏來了新員工(無論多少),前臺MM也會在收到寄給新員工的快遞後覈實並代爲簽收。這裏有2層意思的:函數
- 如今委託前臺的同事是能夠代爲簽收的,即程序中的現有的dom節點是有事件的
- 新員工也是能夠被前臺MM代爲簽收的,即程序中新添加的dom節點也是有事件的
通常來講,DOM須要有事件處理程序,直接給它設事件處理程序就行了!若是是不少的DOM(好比有100個li)須要添加事件處理呢?每一個li都有相同的click點擊事件,可能會用for循環的方法來遍歷全部的li,而後給它們添加事件,那這麼作會存在什麼影響呢?性能
- 減小內存消耗:每一個函數都是一個對象,對象越多內存佔用率就越大,天然性能就越差了。如100個li就要佔用100個內存空間。若是用事件委託,那麼就能夠只對它的父級
<ul>
這一個對象進行操做,這樣須要一個內存空間就夠了。- 減小DOM操做:在JS中,添加到頁面上的事件處理程序數量將直接關係到頁面的總體運行性能,由於須要不斷的與dom節點進行交互。訪問dom的次數越多,引發瀏覽器重繪與重排的次數也就越多,就會延長整個頁面的交互就緒時間。若是要用事件委託,就會將全部的操做放到js程序裏面,與dom的操做就只須要交互一次,這樣就能大大的減小與dom的交互次數,提升性能。
因爲事件會在冒泡階段向上傳播到父節點,所以能夠把子節點的監聽函數定義在父節點上,由父節點的監聽函數統一處理多個子元素的事件。適合用事件委託的事件:click、keydown、keyup、keypress。不適合的主要分爲兩大類:一類是沒有冒泡機制的,如focus、blur;另外一類是每次都要計算它的位置而很是很差把控的如mousemove。
DOM 的事件操做(監聽和觸發),都定義在EventTarget
接口。全部節點對象都部署了這個接口,其餘一些須要事件通訊的瀏覽器內置對象(如XMLHttpRequest、
AudioNode、
AudioContext)也部署了這個接口。
EventTarget.addEventListener()
用於在當前節點或對象上,定義一個特定事件的監聽函數,一旦這個事件發生,就會執行監聽函數。該方法能夠爲當前對象的同一個事件添加多個不一樣的監聽函數:這些函數按照添加順序觸發;若是爲同一個事件屢次添加同一個監聽函數,該函數只會執行一次,多餘的添加將自動被去除。該方法接受三個參數:
type
:事件名稱,大小寫敏感。
listener
:監聽函數。事件發生時會調用該監聽函數。該參數除了監聽函數,還但是一個具備handleEvent
方法的對象。監聽函數內部的this
指向當前事件所在的那個對象。
useCapture
:布爾值,表示監聽函數是否在捕獲階段(capture)觸發,默認爲false
(監聽函數只在冒泡階段被觸發)。該參數可選外,還但是一個屬性配置對象。該對象有如下屬性:
once
:布爾值,表示監聽函數是否只觸發一次,而後就自動移除。capture
:布爾值,表示該事件是否在捕獲階段
觸發監聽函數。passive
:布爾值,表示監聽函數不會調用事件的preventDefault
方法。若是監聽函數調用了,瀏覽器將忽略這個要求,並在監控臺輸出一行警告。
EventTarget.removeEventListener
方法用來移除addEventListener
方法添加的事件監聽函數。注意若是該方法要生效,其三個參數必須與addEventListener
方法徹底一致。
EventTarget.dispatchEvent
方法在當前節點上觸發指定事件,從而觸發監聽函數的執行。該方法返回一個布爾值,只要有一個監聽函數調用了Event.preventDefault()
,則返回值爲false
,不然爲true
。該方法的參數是一個Event
對象的實例。
事件發生之後,會產生一個事件對象,做爲參數傳給監聽函數。瀏覽器原生提供一個Event
對象,全部的事件都是這個對象的實例,如鼠標事件、鍵盤事件、觸摸事件、表單事件等,詳情見這裏。
Event
構造函數接受兩個參數。第一個參數type
是字符串,表示事件的名稱;第二個參數options
是一個對象,表示事件對象的配置。該對象主要有下面兩個屬性。
bubbles
:布爾值(可選),默認爲false
,表示事件對象是否冒泡。cancelable
:布爾值(可選),默認爲false
,表示事件是否能夠被取消,即可否用Event.preventDefault()
取消這個事件。一旦事件被取消,就好像歷來沒有發生過,不會觸發瀏覽器對該事件的默認行爲。Event.type
屬性返回一個字符串,表示事件類型。
Event.bubbles
屬性返回一個布爾值,表示當前事件是否會冒泡。
Event.eventPhase
屬性返回一個整數常量,表示事件目前所處的階段。返回值有四種可能:
- 0:事件目前沒有發生
- 1:事件處於捕獲階段
- 2:事件到達目標節點
- 3:事件處於冒泡階段
Event.target
屬性返回原始觸發事件的那個節點,即事件最初發生的節點。
Event.currentTarget
屬性返回事件當前所在的節點,即事件當前正在經過的節點。
Event.preventDefault
方法取消瀏覽器對當前事件的默認行爲。好比點擊連接後,瀏覽器默認會跳轉到另外一個頁面,使用這個方法之後,就不會跳轉了。注意該方法只是取消事件對當前元素的默認影響,不會阻止事件的傳播。Event.stopPropagation
方法阻止事件在 DOM 中繼續傳播,防止再觸發定義在別的節點上的監聽函數,可是不包括在當前節點上該事件其餘監聽函數執行狀況。Event.stopImmediatePropagation
方法阻止事件在 DOM 中繼續傳播,防止再觸發定義在別的節點上的監聽函數,同時也包括在當前節點上該事件其餘監聽函數再也不執行。