你真的理解事件冒泡和事件捕獲嗎?

最近在複習前端的基礎,看到事件這一節的時候,恰好發現了筆記中一道特別好玩而且十分有趣的代碼,根據這麼一道題目,基本上可以把事件冒泡和事件捕獲的盲區給掃空。本文就帶你一塊兒來看看這段有趣的代碼。javascript

可是,首先咱們仍是要例行公事,把一些基礎的概念過一過,其實也不須要太長的時間。css

什麼是事件?

JavaScriptHTML之間的交互是經過事件實現的。事件,就是文檔或瀏覽器窗口發生的一些特定的交互瞬間。可使用監聽器(或事件處理程序)來預約事件,以便事件發生時執行相應的代碼。通俗的說,這種模型其實就是一個觀察者模式。(事件是對象主題,而這一個個的監聽器就是一個個觀察者)

事件流

事件流描述的就是從頁面中接收事件的順序。而 IENetscape提出了徹底相反的事件流概念。 IE事件流是事件冒泡,而 Netscape的事件流就是事件捕獲。

打個比喻,若是你把手指放在圓心上(多個同心圓),那麼你的手指指向的不是一個圓,而是紙上全部的圓。若是你單擊了某個按鈕,在單擊按鈕的同時,也單擊了按鈕的容器元素,甚至是整個頁面。html

事件冒泡

IE的事件流叫作事件冒泡。即事件開始時由最具體的元素(文檔中嵌套層次最深的那個節點)接收,而後逐級向上傳播到較爲不具體的節點(文檔)。全部現代瀏覽器都支持事件冒泡,而且會將事件一直冒泡到 window對象。

事件捕獲

事件捕獲的思想是不太具體的節點應該更早的接收到事件,而在最具體的節點應該最後接收到事件。事件捕獲的用以在於 事件到達預約目標以前捕獲它。 IE9+、Safari、Chrome、OperaFirefox支持,且從 window開始捕獲(儘管 DOM2級事件規範要求從 document)。因爲老版本瀏覽器不支持,因此不多有人使用事件捕獲。

DOM事件流

「DOM2級事件」規定事件流包括三個階段,事件捕獲階段、處於目標階段和事件冒泡階段。首先發生的事件捕獲,爲截獲事件提供了機會。而後是實際的目標接收了事件。最後一個階段是冒泡階段,能夠在這個階段對事件作出響應。

以上這段話,就是咱們DOM事件流的根本了,這段話很是重要,雖然看似簡單,可是裏面包含着不少小細節,我會在下面例子中所有講到。前端

一個十分有趣的例子

如今就開始來說解這個有趣的例子,先把代碼貼上來,大體的結構十分簡單。因爲segmentfault代碼過長時會有滾動條,這裏我把它切分,並省略了一些沒必要要的結點,便於觀看。java

<div id="a">
    <div id="b">
        <div id="c"></div>
    </div>
</div>
#a{
    width: 300px;
    height: 300px;
    background: pink;
}
#b{
    width: 200px;
    height: 200px;
    background: blue;
}
#c{
    width: 100px;
    height: 100px;
    background: yellow;
}
var a = document.getElementById("a"),
    b = document.getElementById("b"),
    c = document.getElementById("c");
c.addEventListener("click", function (event) {
    console.log("c1");
    // 注意第三個參數沒有傳進 false , 由於默認傳進來的是 false
    //,表明冒泡階段調用,我的認爲處於目標階段也會調用的
});
c.addEventListener("click", function (event) {
    console.log("c2");
}, true);
b.addEventListener("click", function (event) {
    console.log("b");
}, true);
a.addEventListener("click", function (event) {
    console.log("a1");
}, true);
a.addEventListener("click", function (event) {
    console.log("a2")
});
a.addEventListener("click", function (event) {
    console.log("a3");
    event.stopImmediatePropagation();
}, true);
a.addEventListener("click", function (event) {
    console.log("a4");
}, true);

整個的html頁面就是下面這三個小盒子。segmentfault

那麼如今有三個問題:瀏覽器

  • 若是點擊c或者b,輸出什麼?(答案是a一、a3

    stopImmediatePropagation包含了stopPropagation的功能,即阻止事件傳播(捕獲或冒泡),但同時也阻止該元素上後來綁定的事件處理程序被調用,因此不輸出 a4。由於事件捕獲被攔截了,天然不會觸發 b、c 上的事件,因此不輸出 b、c一、c2,冒泡更談不上了,因此不輸出 a2函數

  • 若是點擊a,輸出什麼?(答案是 a一、a二、a3

    不該該是 a一、a三、a2 嗎?有同窗就會說:「a一、a3但是在捕獲階段被調用的處理程序的,a2 是在冒泡階段被調用的啊。」這正是要說明的:雖然這三個事件處理程序註冊時指定了truefalse,但如今事件流是處於目標階段,不是冒泡階段、也不是捕獲階段,事件處理程序被調用的順序是註冊的順序。不論你指定的是true仍是false。換句話來講就是如今點擊的是a這個盒子自己,它處於事件流的目標狀態,而既非冒泡,又非捕獲。(須要注意的是,此時的eventPhase爲2,說明事件流處於目標階段。當點擊a的時候,先從document捕獲,而後一步步往下找,找到a這個元素的時候,此時的targetcurrentTarget是一致的,因此認定到底了,不須要再捕獲了,此時就按順序執行已經預約的事件處理函數,執行完畢後再繼續往上冒泡...)spa

  • 若是註釋掉event.stopImmediatePropagation,點擊c,會輸出什麼?(答案是 a一、a三、a四、b、c一、c二、a2

    若是同一個事件處理程序(指針相同,好比用 handler 保存的事件處理程序),用 addEventListenerattachEvent綁定屢次,若是第三個參數是相同的話,也只會被調用一次。固然,若是第三個參數一個設置爲true,另外一個設置爲false,那麼會被調用兩次。
    而在這裏,都是給監聽函數的回調賦予了一個匿名函數,因此其實每一個處理函數都會被調用。須要注意的是,若是你還不明白爲何在c上觸發的先是c1再是c2的話,那麼你就須要在去看看第二個問題所描述的內容了。設計


以上,就是本文章的內容,文章最後的例子是從網上某個地方看到的,可是具體的出處已經忘記了,若是有同窗找到了出處,煩請告知。

參考資料:《JavaScript高級程序設計》

相關文章
相關標籤/搜索