內嵌模式
事件是和元素相關聯的,將它們以屬性的形式添加到元素中。以屬性的形式爲HTML元素添加屬性的方法,有時稱爲內聯模式(inline model)或內聯註冊模式。
例如:
<body onload="var i=23; i*=3; alert(i);"> //直接將句柄的Javascript代碼寫在這裏
<body onload="calcNumber();"> //更常見的做法,調用一個函數
缺點:如果要爲HTML元素添加事件,需要修改Javascript函數名或行爲,由於指定事件是在不同頁面中中將某個函數指定成事件句柄的,因此需要找到每個頁面、每個元素,手動完成這些修改。也不符合行爲與結構分離的原則。
傳統模式
DOM Level 0事件處理
通過對象屬性將一個函數指派爲事件句柄的做法稱爲傳統模式或傳統註冊模式。像訪問每個對象的屬性那樣直接訪問事件句柄。能在所有的瀏覽器中工作。
例:
window.onload = calcNumber; //爲window對象的onload事件屬性指派一個函數
缺點:你定義一個方法值之後,任何後續的定義都會覆蓋前面定義的值。
例如:
x.onclick = doThis;
x.onclick = doThat;
當用戶點擊x元素的時候只有doThat()函數被執行。如果想讓兩種方法都執行,可以這樣:
x.onclick = function(){
doThis();
doThat();
}
但是這會導致this關鍵字在doThis()和doThat()裏指向錯誤的對象的引用,因爲函數並不是作爲x對象的方法被執行。
DOM Level 2事件模型
W3C的高級事件處理程序註冊模型——事件捕獲和事件冒泡能有效的解決覆蓋的問題。這些模型允許你在相同的元素上爲相同的事件定義任意數量的事件處理程序。
W3C模型
x.addEventListener(‘click’,doThis,false);
x.addEventListener(‘click’,doThat,false);
第一個參數:字符串形式的事件名稱,沒有on前綴。
第二個參數:所執行的函數(沒有圓括號,不希望馬上執行它,只希望在事件發生時執行)
第三個參數:布爾值,表示是事件冒泡(false)還是事件捕獲(true)。(使用冒泡的情況多)
微軟模型
x.attachEvent(‘onclick’,doThis);
x.attachEvent(‘onclick’,doThat);
第一個參數:字符串形式的事件名稱
第二個參數:所執行的函數
現在當用戶點擊元素x的時候,doThis()和doThat()兩者都會執行。不過它們不一定會按照註冊的先後順序去執行。
移除事件處理程序
x.removeEventListener(‘click’,doThis,false);//W3C
x.detachEvent(‘onclick’,doThis);//MS
缺點:沒有辦法找出在一個元素上已經註冊了哪些事件處理程序。這意味着你不能“從元素x上移除所有的onclick事件處理程序”。在傳統模式中,是可能的:x.onclick = null;
最佳方式
我傾向於使用傳統模型,因爲它很簡單而且完全跨瀏覽器,並且this關鍵字總是能正確工作。當然也有不該使用的情況,在那些情況下,我使用addEventListener()。
當頁面中的所有腳本都由我完全控制的時候我會使用傳統模型。因爲不會有其他腳本的介入,所以可以安全的使用傳統模型。
當由多個腳本共存於同一個頁面的時候,我使用addEventListener()。
(摘自ppk on Javascript)
W3C和微軟模型之間的區別
1. 微軟模型不支持事件捕獲。不是一個大問題,事件捕獲很少用。
2. 微軟的模型把事件處理函數視爲一個全局函數,而不是(該事件處理函數)被註冊到的HTML元素的一個方法。是一個嚴重的問題。
由於第二個區別,要使用到Event對象的target和srcElement等屬性來找到目標對象。(個人觀點)
Event Object
之所以寫成英文而不是漢語“Event對象”或“事件對象”是因爲我發現
HTML DOM Event對象頁面http://www.w3school.com.cn/htmldom/dom_obj_event.asp
和與之對應的英文頁面HTML DOM Events http://www.w3schools.com/jsref/dom_obj_event.asp
有些地方不同,兩者頁面知識點數量是相同的,我認爲是中文翻譯帶來的問題,前者頁面中Event相關的內容晦澀難懂。
首先從頁面標題來說,後者的Events加了“s”,應該是“HTML DOM 事件”的意思,但中文翻譯不出來加“s”的複數形式。這個章節中有HTML DOM 事件(Mouse Events鼠標事件、Keyboard Events鍵盤事件等,注意這些Events都加了“s”)。另外還有Event Object,即“事件對象”,這個對象有一些方法和屬性和事件。建議查閱第二個網站,對事件和對象的分類清晰容易讓人明白。
取得event對象
W3C模型中,事件對象作爲第一個參數被傳遞到當前的事件處理程序中;在ms模型中,事件對象總是window.event。
這個兼容性問題一行代碼就能搞定,例如:
document.addEventListener(‘click’,handleEvent,false);
document.attachEvent(‘onclick’,handleEvent);
function handleEvent(e){
var evt = e || window.event;//也可用一個三元操作符
}
找到目標對象
當一個事件發生的時候,通常你會想要操作產生事件的那個元素。要想這麼做,就要先找到這個元素。
有兩個重要的用於找到目標對象的屬性。第一個是事件對象的W3C的target或MS的srcElement屬性,它們總是指向產生事件的元素。第二個是this關鍵字,它一般(但並不總是)指向你在其上定義事件管理器的那個元素。
function handleEvent(){
var evt = e || window.event;
var evtTarget = evt.target || evt.srcElement;
}
其實,Event對象是Javascript 二級DOM事件模型的核心,該模型就是圍繞Event對象完成一些列動作的。添加監聽器——>取得event對象——>操作Event對象的方法和屬性及其他動作(——>取消監聽器)等等。
W3C模型
在W3C模型中,捕獲和冒泡都會發生。當一個事件觸發時,它先被文檔捕獲,然後沿着文檔樹向下遊歷,知道事件目標爲止,這叫做捕獲階段。一旦事件達到它的目標,冒泡過程就開始了。在這個過程中,事件會沿原路的反方向向上遊歷,知道文檔爲止。
事件捕獲和冒泡
例:
- <script type="text/javascript">
- function setup() {
- //事件捕獲
- document.addEventListener('click',cascadeDown,true);
- document.forms[0].addEventListener('click',cascadeDown,true);
- document.forms[0].elements[0].addEventListener('click',cascadeDown,true);
- //事件冒泡
- document.addEventListener("click",bubbleUp,false);
- document.forms[0].addEventListener("click",bubbleUp,false);
- document.forms[0].elements[0].addEventListener("click",bubbleUp,false);
- }
- function cascadeDown(evnt){
- alert("Capturing: " + this);
- }
- function bubbleUp(evnt){
- alert("Bubbling: " + this);
- }
- window.onload = setup;
- </script>
- <body>
- <form style="background-color:#f00; width:100px; height:100px; padding:10px" action="">
- <p>
- <input type="submit" value="Submit" /><br />
- </p>
- </form>
- </body>
順序顯示出以下消息:
Capturing: [object HTMLDocument]
Capturing: [object HTMLForm]
Capturing: [object HTMLInputElement]
Bubbling: [object HTMLInputElement]
Bubbling: [object HTMLFormElement]
Bubbling: [object HTMLDocument]
當點擊Submit按鈕時,事件從文檔一級開始,向下遊歷至表單域按鈕,在這個過程中依次調用document、表單和提交按鈕的事件句柄。然後事件進入冒泡階段,向上遊歷到文檔一級,先後調用提交按鈕、表單、document元素的事件句柄。
傳統模型與微軟模型只支持事件冒泡,不支持事件捕獲。
取消事件冒泡機制
- document.getElementById(“first”).onmousedown = function(evnt){
- var theEvent = evnt ? evnt : window.event;
- alert(“first element event”);
- stopEvent(theEvent);
- }
- function stopEvent(evnt){
- if(evnt.stopPropagation){
- evnt.stopPropagation();
- }else{//IE
- evnt.cancelBubble = true;
- }
- }
結束對上例中針對最外層div元素對click事件句柄的調用,前兩個alert對話框仍然會顯示。但是最後一個針對document事件的alert對話框將不會顯示,因爲在事件到達棧頂最頂部之前,事件已經被取消了。
事件句柄和this
上例中程序通過onload事件句柄在頁面載入完成時調用calcNumber函數,觸發的事件是load,它是和瀏覽器的window對象關聯的。事件句柄也叫鉤子(hook)。calcNumber叫事件句柄函數。
this關鍵字表示的是當前調用的函數或方法的所有者。對於一個全局函數而言,它表示的就是window對象。對於一個對象的方法而言,它表示的就是該對象實例。而在一個事件句柄中,它表示的就是接收到該事件的元素。
例,出現在第一個div元素的onclick事件句柄函數中的this指向的就是div元素本身。
- document.getElementById(“first”).onmousedown = function(){
- alert(this);//輸出“[object HTMLDivElement]”
- }
- x.onclick = function(){
- test();
- }
- function test(){
- this.style.backgroundColor = ‘#cc0000’;
- }
這種情況下test函數是通過全局對象來調用的,所以this指向的是對全局對象window的引用。