細說addEventListener與事件捕獲、事件冒泡

細說addEventListener與事件捕獲、事件冒泡

(一)addEventListener的基本用法

在複雜的項目開發中,javascript和html的解耦變得至關重要,我們被推薦使用事件動態綁定的方式來處理按鈕的事件。W3C爲我們提供了addEventListener()函數用來爲指定的dom元素動態綁定事件。這個函數有三個參數:
type:用來設置時間類型,例如click
listener:用來設置監聽事件的函數,及type類型的事件發生後執行的函數

大部分情況下,確切的說是我們綁定事件的元素的父元素和子元素都不存在操作類型相同的觸發事件時,前兩個參數就可以滿足我們爲按鈕綁定事件的需求。
比如:

function sayHello() {
    console.log("hello");
}

var myDiv = document.getElementById("myDiv");
myDiv.addEventListener("click", sayHello);

這樣我們點擊id爲myDiv的元素時,控制檯就會輸出”Hello”。

(二)事件冒泡

但是,當出現如下情況時,情況就變得有些複雜。
下面的html中存在“四世同堂”的個元素(這裏我省略了它們的CSS樣式)運行效果如圖所示

<div id="grandpa" style="...">grandpa
    <div id="father" style="...">father
        <div id="child" style="...">child
            <div id="grandson" style="...">grandson</div>
        </div>
    </div>
</div>

這裏寫圖片描述
最外層褐色的是id爲grandpa的div,其後粉色的是id爲father的div,藍色的是id爲child的div,最裏層黑色的是id爲grandson的div。(子元素依次置於父元素之中且逐級變小)
下面設置了四個函數用來進行事件綁定:

    function sleep() {
        console.log("grandpa: I am sleeping,well I just can't fall asleep");
    }

    function watchTV() {
        console.log("father:I am watching TV");
    }

    function playingCard() {
        console.log("child:I am playing Card");
    }

    function doingHomework() {
        console.log("grandson:I am doing my homework T_T");
    }

使用下面的代碼,我們可以獲取四個元素對應DOM

   var grandpa = document.getElementById("grandpa");
   var father = document.getElementById("father");
   var child = document.getElementById("child");
   var grandson = document.getElementById("grandson");

現在,我試着同時分別爲grandpa和grandson綁定sleep和doingHomework事件。

  grandpa.addEventListener("click", sleep);
  grandson.addEventListener("click", doingHomework);

這時我們點擊最外層的grandpa時,當然會觸發sleep函數,然而當我們點擊grandson時,控制檯的輸出如下:
這裏寫圖片描述
這是因爲grandson在grandpa之上,當點擊grandson時,同時也在grandpa上進行了點擊操作,所以在執行了doingHomework後,還會觸發grandpa的sleep函數。
這種當滿足條件後從子元素到父元素依次觸發其上事件的處理方式叫做事件冒泡
我們也爲father和child分別綁定watchTV和playingCard函數

    father.addEventListener("click", watchTV);
    child.addEventListener("click", playingCard);

這時當點擊grandson時,由於事件冒泡原理,顯然,先觸發doingHomework,再執行playingCard,再執行watchTV,最後執行grandpa的sleep。

(三)事件捕獲

JS還設置了另外一種處理多(父子)元素多事件觸發的方式,叫做事件捕獲。事件捕獲與事件冒泡完全相反,先觸發祖先元素的事件,然後再逐級觸發子元素的事件。默認情況下,綁定事件時,採用事件冒泡原則,如果想要進行事件捕獲的話,需要設置一個參數。

(四)addEventListener神祕的第三個參數

我們可以爲addEventListener函數添加第三個參數useCapture,參數值是布爾值,默認是false。當useCapture爲false時,事件處理採取事件冒泡的原則,當userCapture爲true時,則採取事件捕獲的原則,現在我們爲我們“四世同堂”的元素設置第三個參數。

   grandpa.addEventListener("click", sleep, true);
   father.addEventListener("click", watchTV, true);
   child.addEventListener("click", playingCard, true);
   grandson.addEventListener("click", doingHomework, true);

這時,當點擊grandson時,就會先執行祖先元素的事件,再執行後代元素的事件了,控制檯的輸出如下圖所示:
這裏寫圖片描述
雖然默認情況下,useCapture的值是false,但我推薦我們在綁定函數時把它明顯的寫出來以避免瀏覽器兼容性的問題。

(五)事件冒泡與事件捕獲要是同時進行怎麼辦

有思想的同學肯定會思考這樣一個問題,在上述綁定事件的代碼中,第三個參數不是全部設置的true,就是全部設置成false,那如果既有true,又有false,有的元素設置成按事件冒泡處理,有的元素設置成按事件捕獲處理,那怎麼辦呢?
這裏就不調大家的性子了,直接告訴大家答案,我們的瀏覽器更“喜愛”事件捕獲:它會先把useCapture爲false的元素綁定事件放到一邊,按照事件捕獲正常的順序執行useCapture爲true的元素綁定事件,最後在按照事件冒泡順序執行useCapture爲false。
現在我們作如下更改

    grandpa.addEventListener("click", sleep, true);
    father.addEventListener("click", watchTV, false);
    child.addEventListener("click", playingCard, true);
    grandson.addEventListener("click", doingHomework, false);

按照上述原則,當點擊grandson時,先執行useCapture爲true的元素的綁定事件,又按照事件捕獲原則,先執行grandpa的事件,再執行child的事件。之後,再按照事件捕獲順序執行useCapture爲false的事件,輸出結果如下:
這裏寫圖片描述

(六)阻止事件冒泡和捕獲

我們可以利用時間對象event的stopPropagation()方法組織事件的進一步傳播。
我們修改一下doingHomework函數:

    function doingHomework(event) {

        // 阻止事件的進一步傳播,但仍舊會執行接下來的代碼
        event.stopPropagation();
        console.log("grandson:I am doing my homework T_T");
    }

這時我們點擊grandson,控制檯輸出的結果是:
這裏寫圖片描述
發現事件執行到doingHomework就被阻斷了。值得注意的是,event.stopPropagation()函數並不會阻止其下函數內容的執行。
如果你使用的是jquery的事件綁定,也可以直接在函數中使用return false來阻止事件的傳播(當然event.stopPropagation同樣有效),與event.stopPropagation不同的是,return false會阻止同函數下面的代碼執行。這裏就不舉例了。

關於事件的綁定與傳播,就介紹到這裏,希望對大家有幫助。

發佈了42 篇原創文章 · 獲贊 67 · 訪問量 24萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章