冒泡和捕獲

?在瀏覽器中,事件的傳播方式分為:事件冒泡和事件捕獲。那么事件冒泡和事件捕獲分別是什么?為什么會出現兩種傳播方式呢?

一、前提

  1. 在瀏覽器中用戶點擊鼠標,操作系統最先知道,瀏覽器次之。
  2. 頁面中子元素被點擊了,意味著父元素也被點擊。
  3. 如果子元素和父元素同時監聽了點擊事件,那么點擊子元素后,是子元素先知道,還是父元素先知道。而這個知道的前后順序,就是由事件傳播方式決定的。

二、冒泡模式和捕獲模式

?事件的傳播途徑是通過文檔節點組成的有序列表。在DOM里面,這個事件傳播路徑的最后一點就是事件對象(Event Target)本身,其之前的元素就是事件對象的祖先元素

  • 觸發事件后:
    • 對于冒泡模式,事件首先被事件對象本身知道,然后讓其父元素知道,就這樣在祖先元素中一層層向外傳播,直至window 對象,如果中間有節點設置監聽這個事件,就調用其監聽函數。
    • 對于捕獲模式,事件首先被window 對象知道,然后到document 節點,最后才被事件對象本身知道,如果中間有節點監聽此事件,就調用監聽函數。
      例子:
<html>
        <head>
            <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
            <meta charset="utf-8">
            <title>example</title>
        </head>
        <body>
            <ul>
                <li>item1</li>
                <li>item2</li>
                <li class="clicked">item3</li>
            </ul>
        </body>
    </html>
  • 在上面例子的結構中,假設我在item3 用鼠標點擊了一下。
    • 在捕獲模式中,事件是按以下順序傳播的:window --> document --> <html> --> <body> --> <ul> --> <li class="clicked">,如果有節點監聽點擊事件,那么就在傳播到那個節點的時候調用事件函數。
    • 在冒泡模式中,事件的傳播順序是同捕獲模式相反的:<li class="clicked"> --> <ul> --> <body> --> <html> --> document --> window,如果有節點監聽點擊事件,那么就在傳播到那個節點的時候調用事件函數。

三、設置冒泡模式或捕獲模式

?在十幾年前,那時候瀏覽器各自為政,其中一個巨頭網景公司主張捕獲模式,所以那時它的瀏覽器只支持捕獲模式;而另一巨頭微軟公司支持冒泡模式,所以IE 9 以前的版本只支持冒泡模式。在2000 年的時候,w3c 將兩種模式都寫入標準里面,所以現在大部分瀏覽器都支持兩種模式。

?我們可以使用addEventListener(type, listener, useCapture) 方法來設置事件的傳播模式,如果想要捕獲模式,就將第三個參數useCapture 設為true 。如果想要冒泡模式,可以將第三個參數設為false 或者直接省略第三個參數,因為不傳入參數的時候,參數值為undefined

? 例子:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>JS Bin</title>
</head>
<body>
    <div>1
        <div>2
            <div>3
                <div>4
                    <div>5</div>
                </div>
            </div>
        </div>
    </div>
    <section id="alog"></section>
    <script>
        var divs = document.getElementsByTagName('div');
        function capture() {
            log('capture: ' + this.firstChild.nodeValue.trim())
        }
      
        function bubble() {
            log('bubble: ' + this.firstChild.nodeValue.trim())
        }

        for (var i = 0; i < divs.length; i++) {
            divs[i].addEventListener('click', capture, true);
            divs[i].addEventListener('click', bubble, false);
        }

        function log(msg) {
            var p = document.createElement('p');
            p.textContent = msg;
            alog.appendChild(p);
        }
    </script>
</body>
</html>

下圖是我點擊div 5 的結果

?

?在兩種模式混用的情況下,如果是事件對象(Event Target)的父元素,按先捕獲后冒泡的順序進行。如果是事件對像自身,那么誰先監聽就先執行誰。在實際工作中不推薦混用兩種模式。

?例子:對上面代碼進行小小的改動

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>JS Bin</title>
</head>
<body>
    <div>1
        <div>2
            <div>3
                <div>4
                    <div>5</div>
                </div>
            </div>
        </div>
    </div>
    <section id="alog"></section>
    <script>
        var divs = document.getElementsByTagName('div');
        function capture() {
            log('capture: ' + this.firstChild.nodeValue.trim())
        }
      
        function bubble() {
            log('bubble: ' + this.firstChild.nodeValue.trim())
        }

        for (var i = 0; i < divs.length; i++) {
/* 只是調換了下面兩句語句的位置*/
            divs[i].addEventListener('click', bubble, false); 
            divs[i].addEventListener('click', capture, true);
//  其他語句不變
        }

        function log(msg) {
            var p = document.createElement('p');
            p.textContent = msg;
            alog.appendChild(p);
        }
    </script>
</body>
</html>

下圖是我點擊div 5 的結果

鏈接

本文部分借鑒自: https://stackoverflow.com/a/4616720/7309659
部分來自饑人谷老師方方的觀點: http://www.lxweimin.com/users/b4fd9acca45c/timeline
饑人谷鏈接:https://jirengu.com/

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容