事件流,描述的是頁面中接受事件的順序,不過,IE的事件流是事件冒泡流,而Netscape Communicator的事件流是事件捕獲型。標準同時支持兩種事件模型,即捕獲型事件與冒泡型事件,可是,捕獲型事件先發生。javascript
兩種事件流都會觸發DOM中的全部對象,從document對象開始,也在document對象結束(大部分兼容標準的瀏覽器會繼續將事件是捕捉/冒泡延續到window對象)JavaScript與HTML之間的交,經過事件實現。經常使用觀察員模式,使用偵聽器來預約事件,以便事件發生時執行相應的代碼。html
DOM(文檔對象模型)結構是一個樹型結構,當一個HTML元素產生一個事件時,該事件會在元素結點與根節點之間按特定的順序傳播,路徑所通過的節點都會收到該事件,這個傳播過程可稱爲DOM事件流。java
事件句柄與事件監聽器的最大不一樣之處是使用事件句柄時一次只能插接一個事件句柄,但對於事件監聽器,一次能夠插接多個。如今大多數瀏覽器都內置了一些更高級的事件處理方式,即,事件監聽器,這種處理方式就不受一個元素只能綁定一個事件句柄的限制。瀏覽器
HTML以下:dom
body id="myBody"> <button id="myButton">click</button> </body>
在IE中,每一個元素和window對象都有兩個方法:attachEvent方法和detachEvent方法。函數
IE 事件對象使用 event 的 srcElement 屬性獲取事件目標。性能
var btn = document.getElementById("myButton"); btn.attachEvent("onclick", function(event) { alert(event.srcElement.id); // myButton });
addEventListener方法接受三個參數。第一個參數是事件類型名,值得注意的是,這裏事件類型名稱與IE的不一樣,事件類型名是沒’on’開頭的;第二個參數eventListener是回調處理函數(即監聽器函數);第三個參數註明該處理回調函數是在事件傳遞過程當中的捕獲階段被調用仍是冒泡階段被調用 ,一般此參數一般會設置爲false(爲false時是冒泡),若是將其值設置爲true,那就建立一個捕捉事件監聽器。this
經過addEventListener方法添加的事件處理函數,必須使用removeEventListener方法才能刪除,並且要求參數與添加事件處理函數時addEventListener方法的參數徹底一致(包括useCapture參數),不然將不能成功刪除事件處理函數。設計
標準事件對象使用 event 的 target 屬性獲取事件目標:代理
var btn = document.getElementById('myButton'); btn.addEventListener("click", function(event) { alert(event.target.id); // myButton });
<div id="oDiv"> <a id="oA" href="http://baidu.com"> 百度一下,你就知道。 </a> </div>
正常狀況下,咱們點擊a連接,會跳轉到baidu.com,但當使用了e.preventDefault().
var a = document.getElementById("oA"); //獲取div var oDiv = document.getElementById('oDiv'); //dom0級綁定事件方法 a.onclick=function(e){ e.preventDefault(); } // 舊版本IE不支持捕獲,通常使用冒泡。 oDiv.addEventListener("click", function (e) { e.preventDefault(); }, false);
//關於阻止事件冒泡.ie678用attachEvent a.addEventListener("click", function (e) { alert(1); }, false);//冒泡 oDiv.addEventListener("click", function (e) { alert(2) }, false);//冒泡 oDiv.addEventListener("click", function (e) { alert(3); }, true);//捕獲,捕獲先發生
第3個參數設置爲false時候爲冒泡,先捕獲,後冒泡。這裏彈出順序依次是:3,1,2
//關於阻止事件冒泡.ie678用attachEvent a.addEventListener("click", function (e) { e.stopPropagation();//阻止冒泡/捕獲 alert(1); }, false);//冒泡 oDiv.addEventListener("click", function (e) { alert(2) }, false);//冒泡 oDiv.addEventListener("click", function (e) { e.stopPropagation(); alert(3); }, true);//捕獲,捕獲先發生
這時候順序是:3,不會出現1,2。由於在第3個函數裏用到了stopPropagation,並且捕獲比冒泡事件先發生。因此只會出現3.
addEventListener在支持DOM2的瀏覽器中使用,如FF, Chrome等
attachEvent爲IE所用
addEventListener的第三個參數爲true時,在捕獲階段執行,爲false時,在冒泡階段執行
attachEvent的均在冒泡階段執行
addEventListener的做用域爲元素做用域,this爲element的引用
addEvent的爲全局做用域,this爲window的引用
addEventHander:執行順序與添加順序一致
attachEvent:執行順序與添加順序相反
attachEvent中的事件帶on addEventListener中的事件不帶on
例如:有一個10列、100行的HTML表格,假若有一次你須要讓表格中的每個單元格在被點擊的時候變成可編輯狀態。
若是把事件處理器加到這1000個單元格會產生一個很大的性能問題,而且有可能致使內存泄露甚至是瀏覽器的崩潰。
相反地,使用事件代理的話,只須要把一個事件處理器添加到table元素上就能夠了,這個函數能夠把點擊事件給截下來,而且判斷出是哪一個單元格被點擊了。
好比:有一個 table元素,ID是「report」,咱們爲這個表格添加一個事件處理器以調用editCell函數。editCell函數須要判斷出傳到table 來的事件的目標元素。考慮到咱們要寫的幾個函數中都有可能用到這一功能,因此咱們把它單獨放到一個名爲getEventTarget的函數中:
function getEventTarget(e) { e = e || window.event; return e.target || e.srcElement; }
e這個變量表示的是一個事件對象,咱們只須要寫一點點跨瀏覽器的代碼來返回目標元素,在IE裏目標元素放在srcElemtn屬性或event.toElement屬性中,而在其它瀏覽器裏則是target或event.relatedTarget屬性。
接下來就是editCell函數了,這個函數調用到了 getEventTarget函數。一旦咱們獲得了目標元素以後,剩下的事情就是看看它是不是咱們所須要的那個元素了。
function editCell(e) { var target = getEventTarget(e); if(target.tagName.toLowerCase() === 'td') { // DO SOMETHING WITH THE CELL } }
經過事件冒泡,那些須要建立的以及駐留在內存中的事件處理器少了。這樣咱們就提升了性能,並下降了崩潰的風險。
參考:《JavaScript高級程序設計》