終於學到事件了,不知道為何聽到“事件”就有一種莫名的興奮。可能是之前的那些知識點過於枯燥無味吧,說起事件感覺頓時高大上了。今天我們就來好好分析下這個高大上的東西。 可以說,如果沒有事件我們的頁面就只能閱讀了。有了事件,我們可以通過鍵盤或是滑鼠和頁面交互了,通過我們不同的操作頁面給出不同的響應。 ...
終於學到事件了,不知道為何聽到“事件”就有一種莫名的興奮。可能是之前的那些知識點過於枯燥無味吧,說起事件感覺頓時高大上了。今天我們就來好好分析下這個高大上的東西。
可以說,如果沒有事件我們的頁面就只能閱讀了。有了事件,我們可以通過鍵盤或是滑鼠和頁面交互了,通過我們不同的操作頁面給出不同的響應。好了,開始我們今天的分析吧。
DOM0級事件處理方式
什麼是DOM0級?
其實世上本來沒有DOM0級,叫的人多了就有了DOM0級。
在1998 年 10 月 DOM1級規範成為 W3C 的推薦標準,在此之前的實現我們就習慣稱為DOM0級,其實本是沒有這個標準的。
<input type="button" value="but" id="but" /> <script type="text/javascript"> document.getElementById("but").onclick = function () { alert("點擊了按鈕1"); } document.getElementById("but").onclick = function () { alert("點擊了按鈕2"); } </script>
上面代碼,我們發現點擊按鈕時,僅僅只彈出了“點擊了按鈕2”。上一個定義的方法被覆蓋了。這種會覆蓋上一次事件定義的方式我們稱為DOM0級事件。
如果我們使用Jquery來添加事件的話:
<script src="../Scripts/jquery-1.8.2.js"></script> <input type="button" value="but" id="but" /> <script type="text/javascript"> $("#but").click(function () { alert("點擊了按鈕1"); }); $("#but").click(function () { alert("點擊了按鈕2"); });
我們會發現依次彈出了“點擊了按鈕1”,“點擊了按鈕2”,這是怎麼做到的?為什麼沒有覆蓋上一次的定義,我猜應該是使用到了DOM2級事件機制。(我沒有看過Jquery的源碼,暫時還看不懂)。
DOM2級事件處理方式
<input type="button" value="but" id="but" /> <script type="text/javascript"> document.getElementById("but").addEventListener("click", function () { alert("點擊了按鈕1"); }); document.getElementById("but").addEventListener("click", function () { alert("點擊了按鈕2"); }); </script>
如此通過元素對象的addEventListener添加的方法就是2級事件。這裡需要註意,與0級事件不同的是事件名前面不能帶“on”了。點擊按鈕彈出的結果和前面的Jquery添加方式一樣。
有人可能要問了,怎麼沒有DOM1級事件。我能說的是沒有就是沒有,沒有為什麼。在確定DOM1級標準的時候不需要擴展事件定義機制,DOM0級的事件就夠用了。
我們剛纔通過2級事件添加了方法,那麼如果我們想要刪除其中的一個怎麼辦。如果是上面的那種匿名方法,我可以很明確的告訴你沒辦法移除。下麵我們來說說可以移除的添加方式吧:
<input type="button" value="but" id="but" /> <script type="text/javascript"> document.getElementById("but").addEventListener("click", fun1);//給click事件添加方法fun1 document.getElementById("but").addEventListener("click", fun2);//給click事件添加方法fun2 function fun1() { alert("點擊了按鈕1"); } function fun2() { alert("點擊了按鈕2"); } document.getElementById("but").removeEventListener("click", fun1);//給click事件移除方法fun1 </script>
如此就通過removeEventListener方法進行移除操作了。
以上只是DOM2級的標準實現,當然除了IE這個怪胎非得當攪屎棍。在IE下,有同效的實現函數:
<input type="button" value="but" id="but" /> <script type="text/javascript"> document.getElementById("but").attachEvent("onclick", fun1, false);//給click事件添加方法fun1 document.getElementById("but").attachEvent("onclick", fun2, false);//給click事件添加方法fun2 function fun1() { alert("點擊了按鈕1"); } function fun2() { alert("點擊了按鈕2"); } document.getElementById("but").detachEvent("onclick", fun1);//給click事件移除方法fun1
註意:attachEvent() 和 detachEvent() 替換了addEventListener()和removeEventListener(),且第一個參數是事件名前面加了"on"。
DOM3級事件
可能有人覺得很奇怪,哪來的DOM3級事件啊。其實上面我們說的DOM0級和DOM2級事件說的是事件的處理方式而已,而這裡說的DOM3級事件是說的在DOM3級中新增的一些事件。
至於DOM1級事件,那我就真的沒聽過。
DOM3級事件是在DOM2級事件的基礎上重新定義了或是新增了某些事件。如滑鼠事件:
DOM2有,click、mousedown、mousemove、mouseout、mouseover、mouseup
而DOM3級中卻有,click、dblclick、mousedown、mouseenter、mouseleave、mousemove、mouseout、mouseover、mouseup
(其中dblclick、mouseenter、mouseleave是DOM3中新增的)
DOM3級事件實現方式可以用DOM0和DOM2級中的方式,只是新增了寫事件類型。這裡就不一一列舉了。
事件冒泡
什麼是事件冒泡?我們先來看個例子。
<div onclick="divfun();" style="border:1px dashed red;padding:50px"> <span onclick="spanfun();" style="border: 1px dashed #00ff21; padding: 30px; "> <input type="button" value="but" onclick="butfun();" style="border: 1px dashed #0094ff" /> </span> </div> <script type="text/javascript"> function butfun() { alert("按鈕被點擊了"); } function spanfun() { alert("span被點擊了"); } function divfun() { alert("div被點擊了"); } </script>
一個div包了一個span,然後span裡面包了一個button。我們在點擊按鈕的時候會先觸發按鈕事件,然後觸發span的點擊事件,然後觸發div的點擊事件。這就是事件冒泡。(使用DOM0級事件預設是事件冒泡方式)
效果圖:
示意圖:
事件捕獲
什麼是事件捕獲?其實就是事件冒泡的逆向。
那我們怎麼實現這個效果呢?我們可以通過DOM2級事件。上面我們已經簡單的講解過了DOM2級事件的實現方式
通過addEventListener和removeEventListener給事件添加和刪除函數。上面我們講addEventListener的時候如果你再回頭看看,我們只給了兩個參數,第一個是事件名,第二個是要添加的方法,其實還有第三個參數一個布爾值用來表示事件流方向(true為事件捕獲方向,false為事件冒泡方向)。那麼我們完全可以通過DOM2級事件來實現事件捕獲的效果,如:
<div id="mydiv" style="border:1px dashed red;padding:50px"> <span id="mysapn" style="border: 1px dashed #00ff21; padding: 30px; "> <input id="mybut" type="button" value="but" style="border: 1px dashed #0094ff" /> </span> </div> <script type="text/javascript"> document.getElementById("mydiv").addEventListener("click", divfun, true); document.getElementById("mysapn").addEventListener("click", spanfun, true); document.getElementById("mybut").addEventListener("click", butfun, true); function butfun() { alert("按鈕被點擊了"); } function spanfun() { alert("span被點擊了"); } function divfun() { alert("div被點擊了"); } </script>
效果圖:
同樣,我們也可以通過DOM2級事件實現事件冒泡,就上面的代碼僅僅只需要把addEventListener的第三那個參數改成false就可以了,有興趣的同學可以自己試試。
然後IE這個攪屎棍又開始不服了,我就不實現事件捕獲你能把我怎麼著。IE中等效實現的attachEvent的根本就沒給第三個參數,所以為了相容,我們一般只用事件冒泡(IE只支持事件冒泡)。
事件冒泡的使用
- 取消事件冒泡
通過上面,我們知道只要點擊了按鈕,那麼按鈕的上層元素中的點擊事件都會觸發。那我們會想如果我TM就只想點擊某個元素的時候才 觸發,不想讓它往上冒怎麼辦,請看下麵:
<div id="mydiv" style="border:1px dashed red;padding:50px"> <span id="mysapn" style="border: 1px dashed #00ff21; padding: 30px; "> <input id="mybut" type="button" value="but" style="border: 1px dashed #0094ff" /> </span> </div> <script type="text/javascript"> //第三個參數是false,那麼就是事件冒泡了 document.getElementById("mydiv").addEventListener("click", divfun, false); document.getElementById("mysapn").addEventListener("click", spanfun, false); document.getElementById("mybut").addEventListener("click", butfun, false); function butfun(event) { event.stopPropagation();//在點擊按鈕時,取消事件冒泡 alert("按鈕被點擊了"); } function spanfun() { //這裡沒有取消事件冒泡,所以點擊span的時候還是會繼續冒泡到div alert("span被點擊了"); } function divfun() { alert("div被點擊了"); } </script>
效果圖:
您如果仔細看了效果圖,那麼你會發現。點擊按鈕的時候並沒有冒泡,而點擊span的時候還是冒泡了。那是因為我們代碼裡面在按鈕事件的方法裡面加了 event.stopPropagation()//取消冒泡 。
- 事件委托
既然可以如此,我們可以在每個事件方法裡面都加上取消冒泡,那麼所有的元素都只實現自己的事件對應的方法了。我們發現這個事件冒泡反而把事情搞麻煩了,好好的一個元素對應一個事件幹嘛要冒泡啊,還要手動去取消冒泡。既然有這個東西,它總是有它的作用的。我們看到上面我們定義事件方法時,取到了每個元素,然後給每個元素定義方法。那我們可以通過事件冒泡定義一個方法來實現嗎,先看看下麵的代碼:
<div id="mydiv" style="border:1px dashed red;padding:50px"> <span id="mysapn" style="border: 1px dashed #00ff21; padding: 30px; "> <input id="mybut" type="button" value="but" style="border: 1px dashed #0094ff" /> </span> </div> <script type="text/javascript"> //第三個參數是false,那麼就是事件冒泡了 document.getElementById("mydiv").addEventListener("click", divfun, false); function divfun(event) { var targetID = event.target.id if (targetID == "mybut") { alert("按鈕被點擊了"); } else if (targetID == "mysapn") { alert("span被點擊了"); } else if (targetID == "mydiv") { alert("div被點擊了"); } } </script>
效果圖:
仔細觀察的你,會發現我們點擊每個元素會觸發對應消息。這就是我們上面為每個元素添加方法然後取消冒泡同樣的效果。那麼我們再來分析下實現代碼,會發現這裡反而是使用了冒泡。
event.target//返回事件的目標節點(觸發該事件的節點) var targetID = event.target.id //返回事件的目標節點的id(觸發該事件的節點的Id)
因為我們為最外面的div添加了事件的方法,所以我們在點擊按鈕的時候會依次觸發按鈕、span、div的點擊事件,然按鈕和span都沒有定義事件方法,所以不管是點擊按鈕、span還是div都會冒泡到div的點擊事件,然後我們就可以根據上面的target屬性來得知到底是由那個節點觸發的。請看示意圖:
有人會這,這麼麻煩有什麼好處呢?我們仔細看看這個通過冒泡實現的個節點點擊事件,你有沒有發現我們僅僅只是通過getElementById取了一個最外層的div元素,且我們也僅僅只用了一個方法(雖然方法裡面的邏輯會更加複雜點)。這隻是3個元素,如果有30個呢?甚至上百個呢?我們只定義最外層的元素,這樣就減少了大量的DOM引用了(這樣就直接減少了檢索元素需要花的時間),同時也可以減少記憶體的占用。直接提升了性能。(雖然很多時候我們不會這樣來定義事件,我自己就很少這樣來定義事件,可能是習慣問題吧。>_<)
這是學習記錄,不是教程。文中錯誤難免,您可以指出錯誤,但請不要言辭刻薄。
原文首鏈:http://www.haojima.net/zhaopei/531.html
本文已同步至目錄索引:一步步學習javascript