關於DOM2級事件的事件捕獲和事件冒泡

當瀏覽器開發到第四代的時候,瀏覽器的開發團隊遇到了一個很有意思的問題:頁面的哪一部分會擁有特定的事件?要明白這個問題問的是什麼,我們可以想下面的一個問題,加入我們在一張紙上畫了一組同心圓.如果把手放在紙上,那麼手指指向的不是那一個特定的圓而是紙上所有的圓.兩家瀏覽器開發團隊在看待瀏覽器事件方面還是一致的.如果我們單擊 某個按鈕,他們都認爲單擊事件並不僅僅發上在按鈕身上.換句話說,你也同時單擊了按鈕的容器元素,甚至也單擊了整個頁面.

事件流描述的就是從頁面中接收事件的順序.但是IENetspace 開發團隊提出了差不多但是完全相反的事件流的概念.

  1. IE 的事件流是事件冒泡流
  2. Netspace 的事件流是事件捕獲流

1.事件冒泡

IE 的事件流叫做事件冒泡流,即事件開始由最具體的元素(文檔中嵌套層次最深的那個節點)接收,然後逐級向上傳播到不太具體的節點(文檔)

事件默認是冒泡的,子元素觸發事件時,會沿着DOM結構一層一層向上傳遞,這個過程稱爲事件冒泡。
注意:子元素被定位到了其他位置(視覺上脫離了父元素),也會見事件一層一層向上傳遞

看下面的例子:

在這裏綁定事件用的是 DOM2級事件,它接收三個參數:

  1. 要處理的事件名
  2. 作爲事件處理程序的函數
  3. 布爾值(true,表示在捕獲階段調用事件處理程序,false:表示在事件冒泡階段調用事件處理程序)
 <style>
        * {
            margin:0;
            padding:0;
        }
        #orange{
            position: relative;
            width: 300px;
            height: 300px;
            margin:100px auto;
            background-color: orange;
        }

        #purple {
            position: absolute;
            top:0;
            left:0;
            right:0;
            bottom:0;
            margin:auto;
            width: 180px;
            height: 180px;
            background-color: purple;
        }

        #blue{
            position: absolute;
            top:0;
            left:0;
            right:0;
            bottom:0;
            margin:auto;
            width: 80px;
            height: 80px;
            background-color: #58a;
        }
    </style>   
</head>
<body>
    <div id="orange">
        <div id="purple">
            <div id="blue"></div>
        </div>
    </div>
    <script>
       document.getElementById('orange').addEventListener('click', function(){
            console.log("我是橘色盒子");
        }, false);

        document.getElementById('purple').addEventListener('click', function(){
            console.log("我是紫色盒子");
        }, false);
        
        document.getElementById('blue').addEventListener('click', ()=>{
            console.log("我是藍色盒子");
        }, false);
    </script>

在頁面中的顯示如下圖:
在這裏插入圖片描述
當我點擊最中心的藍色盒子時會打印的信息如下:
在這裏插入圖片描述
當我單擊了頁面中最中心的藍色盒子時,click 事件會按照下面的順序進行傳播:

  1. div#blue
  2. div#purple
  3. div#orange
  4. body
  5. html
  6. doucument

也就是說 click 事件首先在最裏面的藍色盒子上發生,這個元素就是我們開始時單擊的元素,然後 click 事件會沿着 DOM 樹向上傳播,直至傳播到 document 對象:
如下圖所示:
在這裏插入圖片描述
所有的現代瀏覽器都支持事件冒泡,但在具體的實現上還是有一些差別,IE5.5及更早的版本中事件冒泡會跳過<html> 元素(直接從 bodydocument).IE9 FireFox ChromeSafari 則將事件一直冒泡到 window 對象.

2. 事件捕獲

Netspace 團隊提出的另一種事件流叫做 事件捕獲,事件捕獲的思想是不太具體的節點應該更早的接收到事件,而最具體的節點應該最後接收到事件

事件默認是冒泡的,可通過 addEventListener 的第三個參數這隻爲 true 將事件設置爲捕獲,同時,添加了捕獲的事件在使用 removeEventListener 移除時也需要寫第三個參數 true

將上題中的代碼事件監聽中的第三個參數由 false 改爲 true ,觀察一下結果:

       document.getElementById('orange').addEventListener('click', function(){
           console.log("我是橘色盒子");
       }, true);

       document.getElementById('purple').addEventListener('click', function(){
           console.log("我是紫色盒子");
       }, true);

       document.getElementById('blue').addEventListener('click', ()=>{
           console.log("我是藍色盒子");
       }, true);

點擊盒子後打印的信息如下:
在這裏插入圖片描述
我們單擊了頁面中的藍色盒子,那麼元素就會以下列順序觸發 click 事件

  1. document
  2. html
  3. body
  4. div#orange
  5. div#purple
  6. div#blue

在事件捕獲過程中,document 對象首先接受到 click 事件,然後事件沿 dom樹依次向下,一直傳播到事件的實際目標,即 div#blue 元素,下圖就展示了事件捕獲的過程:
在這裏插入圖片描述

3.DOM 事件流

DOM2級事件 規定的事件流包括三個階段:

  1. 事件捕獲階段
  2. 處於目標階段
  3. 事件冒泡階段
    首先發生的是事件捕獲,爲截獲事件提供了機會.然後是實際的目標接受到事件,最後一個階段是事件冒泡階段,這個階段可以對事件作出相應,以上面的案例爲例,點擊最中間的 div#blue 盒子會按下圖所示觸發事件:
    在這裏插入圖片描述
    DOM 事件流中,實際的目標元素(div#blue)在捕獲階段不會接收到事件.這意味着在捕獲階段事件從哪個 windowdocument 再到 htmlbody 一直到 div#purple 就停止了,下一個階段是 “處於目標階段” ,於是在事件 div#blue 上發生,然後冒泡階段發生,事件傳回文檔.

4.阻止默認事件和事件冒泡

默認事件 瀏覽器中很多操作都有默認的行爲,比如右鍵打開菜單,我們稱之爲默認事件。
我們可以通過 DOM 中的事件對象來阻止默認事件.
阻止默認事件:

  1. ev.stopDefault()
  2. return false

event 對象包含與創建它的特定事件有關的屬性和方法,常見的如下:
阻止事件冒泡等.

屬性/方法 類型 說明
preventDefault Function 取消事件的默認行爲.
stopImmediate Propagation() Function 取消事件的進一步捕獲或冒泡,同時阻止任何事件處理程序被調用
stopPropagation Function 取消事件的進一步捕獲或冒泡.

將最開始的案例阻止事件冒泡,改寫如下代碼:

          document.getElementById('blue').addEventListener('click', (ev) => {
            console.log("我是藍色盒子");
        }, false);

改爲:

          document.getElementById('blue').addEventListener('click', (ev) => {
            ev.stopPropagation();
            console.log("我是藍色盒子");
        }, false);

點擊藍色盒子後觀察打印結果:
在這裏插入圖片描述
已經成功阻止事件冒泡.
在平時寫的時候,我通常會將默認事件和事件冒泡都阻止,防止產生不必要的麻煩.

            ev.stopPropagation();
            ev.preventDefault();
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章