BOM基礎(四)

  最近寫的文章感受內容不像以前那麼充實,內容可能也有點雜。對於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);
}

 

  這段代碼跟最前面兩段代碼執行的結果是同樣的,並且他們在googleie8中都能很好的兼容。這樣,咱們就封裝了添加和移除事件的兼容性代碼。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,bodydocument。這是爲何呢?這時候,就涉及了事件冒泡。在說這個以前,咱們先講一講事件執行的階段: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>

 

  在上述代碼中,咱們但願點擊box1box2顯示,點擊文檔讓box2隱藏。按正常的思路來講,咱們會想到上述代碼,不過,在嘗試中咱們發現,無論咱們怎麼點box1box2都不會顯示,這就是由於時間冒泡了,在咱們點擊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;
    }
}

 

   到這裏,這件冒泡就完成了。

相關文章
相關標籤/搜索