事件就是用戶或者瀏覽器自身執(zhí)行的某種動(dòng)作。諸如click、load、和scroll等等,都是事件的名字。而響應(yīng)某個(gè)事件的函就叫做事件處理程序(或事件偵聽器)。事件處理程序的名字都是以”on“開頭,因此ckick事件的事件處理程序就是onclick,load的事件處理程序就是onload。
頁(yè)面中添加事件的有幾種方式
- 直接將代碼寫在HTMl上
<div onclick="alert('Hello World');">Nowamagic</div>
代碼少的還勉強(qiáng)可以,代碼多的話就哭了寫頁(yè)面的哥們了。
- 定義一個(gè)函數(shù),分配給html元素
<script type="text/javascript"> function clk(){} </script> //..... <div onclick="clk()">Div2 Element</div>
這樣做雖然能減少html代碼上的js量,但是這樣子做有幾個(gè)缺點(diǎn):用戶可能在HTMl代碼已經(jīng)出現(xiàn)在頁(yè)面上,但是js事件函數(shù)可能還沒加載進(jìn)來的情況下就點(diǎn)擊了事件對(duì)象元素,從而導(dǎo)致錯(cuò)誤;還有就是HTML與javascript代碼還是未分離完全,如果要修改就要改HTML代碼和javascript代碼兩處
- HTML與javascript代碼完全分離
document.getElementById('myButton').onclick = function(){ alert('Hello!'); } <div id="myButton">點(diǎn)擊按鈕/div>
它只需要HTML元素提供一個(gè)id屬性(或其它能獲取該元素對(duì)象的方式),就可以實(shí)現(xiàn)事件的注冊(cè)。真正做到HTML與javascript代碼完全分離,結(jié)構(gòu)與行為完全分離的事件處理方法。
如何監(jiān)聽事件
DOM Leavl 0
DOM Leavl 0是最早的事件處理形式,它既可以直接寫在HTMl上,也可以把一個(gè)函數(shù)分配給一個(gè)事件處理程序。然而,這種方式給一個(gè)元素的同一事件只允許一個(gè)處理器。因此,我們還要繼續(xù)完善。
W3C DOM Leavl 2 – 事件監(jiān)聽器
通過W3C DEMO Leavl 2事件處理,我們不會(huì)直接把一個(gè)函數(shù)分配給一個(gè)事件處理程序;相反,我們將新函數(shù)添加一個(gè)事件監(jiān)聽器:
var el = document.getElementById('myButton') el.addEventListener( 'click', function(){ alert('Hello!'); }, false)
targetElement.addEventlistener(typeOfEvent,listenerFunction,useCapture);
盡管這個(gè)方法看起來比之前那個(gè)方法復(fù)雜了一點(diǎn),但是那些額外的代碼還是有必要打出來的。對(duì)于DOM級(jí)別2的事件最大的好處就是一個(gè)事件可以注冊(cè)許多處理器。
addEventlistener的頭兩個(gè)參數(shù)是目標(biāo)對(duì)象和事件,不僅如此,函數(shù)最后一個(gè)參數(shù),可以指定處理器是在捕獲階段還是冒泡階段被觸發(fā)(通過設(shè)置”addEventListener()”函數(shù)的第三個(gè)參數(shù)來指定 – true表示在捕獲階段,false表示在冒泡階段)
接下來將針對(duì)W3C DOM Leavl 2事件進(jìn)行討論。
事件在文檔中被傳遞的兩種模型
W3C DOM Leavl 2事件流包括三個(gè)階段:事件捕獲階段、處于目標(biāo)簡(jiǎn)短和事件冒泡階段。
首先我們了解下事件冒泡和事件捕獲。
事件冒泡
在頁(yè)面上有多個(gè)事件,也可以多個(gè)元素響應(yīng)同一個(gè)事件。假設(shè)網(wǎng)頁(yè)上有兩個(gè)元素,其中一個(gè)元素嵌套在另一個(gè)元素中,并且都被綁定了 click 事件,同時(shí) body 也綁定了 click 事件,如下:
js代碼如下:
window.onload = function(){ var oBubble = document.getElementById("oBubble"); var oBubble1 = document.getElementById("oBubble1"); var oBubble2 = document.getElementById("oBubble2"); oBubble.onclick = function(){ alert("Bubble") } oBubble1.onclick = function(){ alert("Bubble1") } oBubble2.onclick = function(){ alert("Bubble2") } }
html代碼如下:
<body id="oBubble">//點(diǎn)擊#oBubble彈出Bubble <div id="oBubble1">//點(diǎn)擊#oBubble1先后彈出Bubble1、Bubble <span id="oBubble2">oBubble</span>//點(diǎn)擊#oBubble2先后彈出Bubble2 、Bubble1、Bubble </div> </body>
事件順序:span→div→body,如下圖所示:
很明顯,每個(gè)元素都會(huì)按照(inside→outside)的冒泡型事件,所以事件冒泡會(huì)引起預(yù)料之外的效果。事件冒泡是IE-DOM處理事件對(duì)象的方法。
事件捕獲
事件捕獲和事件冒泡是剛好相反的兩個(gè)過程,事件捕獲是按照(outside→inside)的冒泡型事件開始觸發(fā)。因此拿以上冒泡例子,alert信息是跟事件冒泡完全相反的順序。
一些注意事項(xiàng):
- W3C DOM Leavl 2標(biāo)準(zhǔn)的addEventListener方法執(zhí)行事件的順序是按照事件注冊(cè)的順序執(zhí)行的。而IE的attachEvent方法則相反–后注冊(cè)的事件先觖發(fā),先注冊(cè)的事件后觸發(fā)。
- W3C DOM Leavl 2標(biāo)準(zhǔn)的瀏覽器文本節(jié)點(diǎn)也會(huì)冒泡,而IE內(nèi)核的瀏覽器文本節(jié)點(diǎn)不會(huì)冒泡。
- W3C DOM Leavl 2瀏覽器事件對(duì)象與IE內(nèi)核的瀏覽器事件不同(具體請(qǐng)參閱)。
- DOM標(biāo)準(zhǔn)的瀏覽器事件卸載方式與IE內(nèi)核的事件卸載方式不同。
接下來我們就來解決跨瀏覽器的事件處理的方案。
跨瀏覽器的事件處理函數(shù)
前面我們介紹了事件冒泡和事件捕獲兩種事件事件獲取方式,而W3C模型 是兩者中和。就是事件發(fā)生時(shí),先從頂層開始進(jìn)行事件捕獲,直到事件觸發(fā)到達(dá)了事件源元素。然后,再?gòu)氖录赐线M(jìn)行事件冒泡,直到到達(dá)document。
利用W3C DOM Leavl 2事件監(jiān)聽器,就是 addEventListener函數(shù)。我們可以自己選擇綁定事件時(shí)采用事件捕獲還是事件冒泡,方法就是綁定事件時(shí)通過addEventListener函數(shù),上面我們介紹過它的三個(gè)參數(shù)了:如果第三個(gè)參數(shù)若是true,則表示采用事件捕獲,若是false,則表示采用事件冒泡。
但是在一個(gè)支持W3C DOM的瀏覽器中,按照DOM Leavl 1綁定事件方式,采用的全都是事件冒泡方式。大多數(shù)時(shí)候,我們也是希望事件從內(nèi)部嵌套的的元素冒泡到外圍元素。
解決方案
以上我們介紹過 W3C DOM Leaval 2事件綁定中的addEventlistener,可以為元素添加多個(gè)事件,而且最后一個(gè)參數(shù)還支持事件冒泡或捕獲,IE6/7/8仍然沒有遵循標(biāo)準(zhǔn)而使用了自己專有的attachEvent,且不支持事件捕獲,所有事件都是發(fā)生在冒泡階段。
所以創(chuàng)建一個(gè)可重用。實(shí)現(xiàn)了DOM Leavl 2事件處理的事件處理函數(shù),但是,它還是要跨瀏覽器。如下經(jīng)典代碼
listenEvent函數(shù)代碼
function listenEvent(eventTarget, eventType, evrntHandler) { if (eventTarget.addEventListener){ eventTarget.addEventListener(eventType, evrntHandler, true);//IE9等其他現(xiàn)代瀏覽器 } else if (eventTarget.attachEvent){ eventType = "on"+eventType; eventTarget.attachEvent(eventType,evrntHandler) //IE6、7、8 } else {eventTarget["on" + eventType] = evrntHandler;}//IE5~ 個(gè)人覺得不寫也罷。 }
listenEvent函數(shù)使用
listenEvent(document,"click",processClick)
此處理函數(shù)接受3個(gè)函數(shù):目標(biāo)對(duì)象、事件(作為一個(gè)字符串),以及函數(shù)名稱。首先測(cè)試對(duì)象,看看它是否支持addEventListener(W3C DOM Leaval 2的事件監(jiān)聽方法),如果支持這個(gè)方法,就把事件映射到事件處理函數(shù)。回到代碼,因?yàn)镮E6、7、8不支持addEventListener,所以檢查是否支持attachEvent,記得前面加”on“,因?yàn)椴患?#8221;on“只是事件的名字,但是因?yàn)镮E6、7、8只支持向上冒泡,所以此方案中 addEventListener的第三個(gè)參數(shù)是false。最后為了兼容DOM leaval 0事件處理,還要加最后一行代碼。
使用W3C DOM Leavl 2處理事件事件監(jiān)聽不會(huì)有覆蓋之前綁定事件的現(xiàn)象,每個(gè)綁定的事件都會(huì)被執(zhí)行,不過 attachEvent 為元素增加的一系列事件不是以添加它們順序執(zhí)行的,而是以相反的順序觸發(fā)。最重要的是,采用事件監(jiān)聽給對(duì)象綁定方法后,可以解除相應(yīng)的綁定。跟以上代碼類似,removeEventListener跟addEventListener對(duì)應(yīng),detachEvent跟eventType對(duì)應(yīng),得到如下解決方案:
stopListening函數(shù)代碼
function stopListening(eventTarget, eventType, evrntHandler) { if (eventTarget.removeEventListener){ eventTarget.removeEventListener(eventType, evrntHandler, true);//IE9等其他現(xiàn)代瀏覽器 } else if (eventTarget.attachEvent){ detachEvent = "on"+eventType; eventTarget.detachEvent(eventType,evrntHandler) //IE6、7、8 } else {eventTarget["on" + eventType] = null;}//IE5~ }
stopListening(document,"click",processClick)
在DOM標(biāo)準(zhǔn)的事件卸載方式中需要注意的是:事件捕獲的參數(shù)。如果你的事件是注冊(cè)在捕獲階段,則卸載事件時(shí),必須將其指定為捕獲階段(true),否則無法卸載;如果你的事件注冊(cè)在注冊(cè)在冒泡階段,則必須將其指定為冒泡階段(false),否則同樣無法卸載。
現(xiàn)在,如果我們想停止監(jiān)聽一個(gè)事件,可以直接調(diào)用stopListening,同樣傳入3個(gè)參數(shù):目標(biāo)對(duì)象、事件和事件處理函數(shù)。
阻止冒泡
因?yàn)榇蟛糠譃g覽器都是按照DOM Leavl 1綁定事件方式,采用的全都是事件冒泡方式。所以阻止事件冒泡(嵌套元素中傳播)是很有必要的。如下方案:
阻止冒泡方案:
function cancelPropagation (event){ event = window.event||event; if( document.all){ event.cancelBubble = true; }else{ event.stopPropagation(); } }
stopListening函數(shù)使用
//這里我們采用上面介紹過的事件處理函數(shù) listenEvent 函數(shù) listenEvent(document.getElementById("oBubble2"),"click",function(evt){ cancelPropagation(evt); })
因?yàn)镮E8、7、6不支持W3C DOM Leavel 2不支持,那么我們就設(shè)置event.cancelBubble 的屬性值為 true;而支持其他現(xiàn)代瀏覽器則使用W3C DOM Leavel 2的,則調(diào)用stopPropagation方法。
最后羅列文章中出現(xiàn)的幾個(gè)Event方法
- W3C DOM Leavl 2綁定和解除事件的方法:addEventListener和removeEventListener:
- IE綁定和解除事件的方法:attachEvent和detachEvent
- 取消事件:W3C DOM Leavl 2使用preventDefault(),iE直接返回false
- 阻止事件在嵌套函數(shù)中傳播(阻止冒泡和捕獲):W3C DOM Leavl 2使用stopPropagation(),IE中cancelBubble返回true
本文出現(xiàn)所有DEMO下載:js-attachEvent.rar