最近寫的文章感受內容不像以前那麼充實,內容可能也有點雜。對於DOM,和BOM來講,要理解是不難的,難的是作的時候。要本身想的到,並且,對於目前階段來講,BOM還存在着很大的兼容性問題,最主要就是要兼容ie8。不過說實在的,用不了多久,ie8也差很少被淘汰了,新版本的ie瀏覽器對標準屬性兼容性仍是很好的。不過接下來要說的主題仍是BOM中的一些內容。前一篇文章中主要講了兩種註冊事件的方式和事件參數。本文主要講如何移除事件,事件的冒泡。html
首先講講移除事件,移除事件用的是removeEventListener();移除事件通常都會配合addEventListener()一塊兒使用,好比下述例子:瀏覽器
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <input type="button" value="click" id="btn"/> <script> var btn = document.getElementById("btn"); btn.addEventListener("click", play, false); function play() { alert("lalala"); btn.removeEventListener("click", play, false); } </script> </body> </html>
咱們給btn這個按鈕添加了鼠標單擊事件,當咱們單擊這個按鈕時,執行play這個函數。在play函數中,咱們先輸出了一句話,以後,咱們再給鼠標移除了執行函數爲play的鼠標單擊事件。這樣,咱們這段代碼就實現了鼠標單擊一次按鈕以後會跳出一句話,但以後鼠標再次點擊的時候就不會有這樣的效果了。不過,這段代碼還有兼容性的問題,就是在ie8及如下版本的瀏覽器中,是沒有這個方法的,在ie8中,咱們註冊事件用的是attachEvent(),他有兩個參數,前一個參數是事件的名稱,第二個參數則是事件處理函數。不過於addEventListener不一樣的是,它的事件名稱是帶on的,也就是說,咱們要用這種方式給元素添加一個鼠標單擊事件的時候,參數名稱要寫成onclick,具體以下:app
btn.attachEvent("onclick", play); function play() { alert("lalala"); btn.detachEvent("onclick", play); }
一樣的,咱們給btn這個按鈕添加了一個鼠標單擊事件,當咱們單擊這個按鈕時,會輸出一句話,以後移除btn的執行函數爲play的鼠標單擊事件。這樣,咱們在ie8中測試,也會發如今單擊鼠標的時候,會顯示一句話,但以後再點擊鼠標則沒有效果了。這樣咱們就實現了在ie8中事件的添加和移除。不過,在實際的使用中,咱們可能在代碼中把這兩段代碼都添加進去,由於這兩種方法能夠說是互不兼容的,google中並不支持attachEvent()這種方法。而ie8及如下版本的瀏覽器則不支持addEventListener這種方法。因此,這時候咱們就只能封裝兼容性代碼了。函數
function addEvent(element, type, listener) { if (element.addEventListener) { element.addEventListener(type, listener, false); } else if (element.attachEvent) { element("on" + type, listener); listener.call(element); } else { element["on" + type]; } } function removeEvent(element, type, listener) { if (element.removeEventListener) { element.removeEventListener(type, listener, false); } else if (element.detachEvent) { element.detachEvent(type, listener); } else { element["on" + type] = null; } }
以上兩個函數分別封裝了添加事件和移除事件的兼容性代碼,在使用的過程當中,只要調用這兩個函數就能夠了。好比下述代碼測試
addEvent(btn, "click", play); function play() { alert("lalala"); removeEvent(btn, "click", play); }
這段代碼跟最前面兩段代碼執行的結果是同樣的,並且他們在google和ie8中都能很好的兼容。這樣,咱們就封裝了添加和移除事件的兼容性代碼。this
說完了添加和移除事件,就來講說事件冒泡了。事件冒泡可能比較難理解,先看代碼瞭解一下什麼是事件冒泡。google
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> #box1 { width: 300px; height: 300px; } #box2 { width: 200px; height: 200px; background-color: #00ff00; } #box3 { width: 100px; height: 100px; background-color: #0000ff; } </style> </head> <body> <div id="box1"> <div id="box2"> <div id="box3"> </div> </div> </div> <script> var box1 = document.getElementById("box1"); var box2 = document.getElementById("box2"); var box3 = document.getElementById("box3"); var elements = [box1, box2, box3, document.body, document]; for(var i = 0, length = elements.length; i < length; i++) { var element = elements[i]; element.addEventListener("click",test , false); function test() { console.log(this); } </script> </body> </html>
上述代碼中,咱們定義了三個盒子,而且給box1,box2,box3,body,document都註冊了鼠標單擊事件。當咱們單擊box3的時候,控制檯中輸出的結果是spa
咱們發現,咱們在點擊box3的時候,不但輸出了box3,還輸出了box2,box1,body跟document。這是爲何呢?這時候,就涉及了事件冒泡。在說這個以前,咱們先講一講事件執行的階段:code
如上圖,事件在被觸發的時候分三個階段,首先是事件捕獲,就是從document起一層層遍歷下來找到事件觸發的元素,而後目標階段,也就是正在執行的當前對象的事件處理程序。當執行玩以後,就會發生事件冒泡。拿上述代碼來講,咱們點擊了box3,在通過了事件捕獲階段以後,就開始執行box3中的執行函數,只時候,就輸出了box3。而後,事件進入到冒泡階段,發現box2也有鼠標單擊事件,這時候,執行box2中的執行函數,因此執行了一次box2中的執行函數,以後事件再冒泡到box1,發現他也有單擊事件,這時候,執行box1中的執行函數,就這樣一級級往上知道document爲止。因此一共輸出了5個內容。這就是事件冒泡。事件冒泡有他的好處,也有他的壞處,先來講說它的好處。既然事件會冒泡,那麼咱們就能夠利用事件冒泡來作一些事情,好比事件委託。htm
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <ul id="names"> <li id="ll">11111</li> <li id="lw">22222</li> <li id="lt">33333</li> <li id="xc">44444</li> </ul> <input type="button" value="插入" id="btn"> <script> var ul = document.getElementById("names"); ul.addEventListener("click", function (e) { alert(e.target.innerText); }, false); var btn = document.getElementById("btn"); btn.onclick = function () { var li = document.createElement("li"); li.innerText = "55555"; ul.appendChild(li); } </script> </body> </html>
在上述代碼中,咱們但願給每一個li註冊一個鼠標點擊事件。按照正常的思路,咱們就會遍歷每一個li,而後給每一個li註冊一個鼠標單擊事件。這樣的方法有不少壞處,首先,若是li不少的話,咱們就要註冊不少個事件,若是咱們使用匿名函數註冊的話,每多一個li就要多一個匿名函數,雖然 咱們能夠在外面先定義一個函數,而後在賦給鼠標單擊事件。這樣能夠增長他的效率。不過,咱們還會遇到另外一個問題,就是咱們若是想動態的建立一個函數的時候,咱們要從新給他們添加事件,這樣會很麻煩。這個時候,若是咱們把這個鼠標單擊事件委託給他們的父元素ul,在點擊ul時,獲取點擊的那個li,這樣,咱們就只要給一個ul註冊事件就行了,不用循環給每一個li來添加事件。這裏用到了一個知識點就是e.target,他獲取的始終是當前觸發事件的元素。這樣,咱們就實現了事件的委託。不過這個方法也是有兼容性問題。在ie8中,咱們只能使用srcElement == target這樣的方式來獲取當前事件觸發的元素。因此,咱們又要封裝兼容性代碼了,不過這個仍是比較簡單的。注意,這邊我就不重複事件參數e的兼容性代碼,這個在我BOM基礎(三)的文章中提到過。
getTarget: function (e) { return e.target ? event.target : e.srcElement; },
既然說了事件冒泡的好處,就該來講說事件冒泡的壞處了。
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <style> #box1 { width: 200px; height: 200px; } #box2 { width: 200px; height: 200px; background-color: red; display: none; } </style> </head> <body> <div id="box1"></div> <div id="box2"></div> <script> var box1 = document.getElementById("box1"); var box2 = document.getElementById("box2"); box1.onclick = function () { box2.style.display = "block"; } document.onclick = function () { box2.style.display = "none"; } </script> </body> </html>
在上述代碼中,咱們但願點擊box1讓box2顯示,點擊文檔讓box2隱藏。按正常的思路來講,咱們會想到上述代碼,不過,在嘗試中咱們發現,無論咱們怎麼點box1,box2都不會顯示,這就是由於時間冒泡了,在咱們點擊box1的時候,執行了它的函數,然box2顯示,當他完成了以後,事件開始冒泡,發現document也有一個鼠標單擊事件,這時候,又執行他裏面的代碼,讓box2隱藏。這個過程很是快,因此咱們根本看不出來box2顯示過。不過這不是咱們想要的效果,因此,咱們就要對其中的過程進行改進,就是在單擊box1的時候,不讓他冒泡。這時候,咱們就又要用到事件參數的一個方法了就是e.stopPropagation();
box1.onclick = function (e) { box2.style.display = "block"; e.stopPropagation(); }
這樣,咱們就阻止了事件冒泡和捕獲,實現了咱們想要的效果,不過,這個方法在ie8中仍是有兼容性問題,他用的是event.cancelBubble=true來阻止事件的冒泡,在ie8中,沒有事件的捕獲這個過程。這時候,咱們又要封裝兼容性代碼了。
function stopPropagation(event) { if (event.stopPropagation) { event.stopPropagation(); } else { event.cancelBubble = true; } }
到這裏,這件冒泡就完成了。