天天看點

事件的捕獲和冒泡

作者:一心一意夢想B6

參考API位址:https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/addEventListener#%E8%AF%AD%E6%B3%95

添加一個監聽事件,首先我們看一下API的文法如下

addEventListener(type, listener);
addEventListener(type, listener, options);
addEventListener(type, listener, useCapture);
           

常用到的是第一個和第三個因為,useCapture預設值是false

useCapture 可選

一個布爾值,表示在 DOM 樹中注冊了 listener 的元素,是否要先于它下面的 EventTarget 調用該 listener。當 useCapture(設為 true)時,沿着 DOM 樹向上冒泡的事件不會觸發 listener。當一個元素嵌套了另一個元素,并且兩個元素都對同一事件注冊了一個處理函數時,所發生的事件冒泡和事件捕獲是兩種不同的事件傳播方式。事件傳播模式決定了元素以哪個順序接收事件。進一步的解釋可以檢視 DOM Level 3 事件及 JavaScript 事件順序文檔。如果沒有指定,useCapture 預設為 false。

這個值的作用有什麼用呢?下面我開始了我的摸索和見解。廢話不多說開始上demo示範。

<!DOCTYPE html>
<html lang="zh-CN>
<head>
    <meta charset=" UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
    div {
        padding: 15px;
        border: 1px solid #e5f3fe;
        position: relative;
        overflow: hidden;
    }

    span {
        background-color: #f2f1f1;
        padding: 8px 15px;
        margin: 15px;
        display: inline-block;
    }

    .description {
        font-size: 22px;
        font-weight: 600;
    }
</style>
</head>

<body>
    <p class="description">
        ok 兄弟你每次點選“節點2” 就會發現這個參數的不同點了。是不是 它的觸發順序出現了不同 對 就是這樣的。
    </p>
    <label>
        <input checked onchange="radioChange(this.value)" name="useCapture" type="radio" id="radio2" value="false" />
        useCapture=false
    </label>
    <label>
        <input onchange="radioChange(this.value)" name="useCapture" type="radio" id="radio1" value="true" />
        useCapture=true
    </label>
    
    <div title="節點1">
        <span>節點1</span>
        <div title="節點2">
            <span>節點2</span>
        </div>
    </div>

    <script>
        //  個人總結:false的話 就是事件冒泡了 從子元素到父元素
        // true的話   就是事件捕獲 從父到子!
        let useCapture = false
        // 添加事件
        function bindEvent() {
            const targets = document.querySelectorAll('div')
            targets.forEach(element => {
                element.addEventListener('click', handleClick, useCapture)
            });
        }
        // 移除事件
        function removeEvent() {
            const targets = document.querySelectorAll('div')
            targets.forEach(element => {
                element.removeEventListener('click', handleClick, useCapture)
            });
        }
        // div click handle
        function handleClick(event){
            // event.stopPropagation()
            alert(this.getAttribute('title'))
        }
        // 處理 radio change
        function radioChange(checked) {
            removeEvent()             
            console.log('radioChange')
            console.log(checked)
            useCapture = checked=="true" ? true : false
            bindEvent()
        }
        // 執行綁定事件
        bindEvent()
    </script>
</body>

</html>           

demo運作起來是這樣的效果

事件的捕獲和冒泡

demo運作起來是這樣的效果

我們發現當div或者出發事件的元素存在嵌套的情況下,這個是控制事件的執行順序的。useCapture=false預設是冒泡,冒泡怎麼了解呢就是水裡的泡泡往上冒麼,那麼就是從子元素王父元素執行。

Capture=true就是捕獲點選一下先相應父級元素的事件,然後傳遞到子元素。也就是先執行父的事件在執行子的事件。

還有一個就是阻止事件冒泡的 event.stopPropagation();

API文檔位址:https://developer.mozilla.org/zh-CN/docs/Web/API/Event/stopPropagation

我們改動一下我們的DEMO再看看

<!DOCTYPE html>
<html lang="zh-CN>
<head>
    <meta charset=" UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
    div {
        padding: 15px;
        border: 1px solid #e5f3fe;
        position: relative;
        overflow: hidden;
    }

    span {
        background-color: #f2f1f1;
        padding: 8px 15px;
        margin: 15px;
        display: inline-block;
    }

    .description {
        font-size: 22px;
        font-weight: 600;
    }
</style>
</head>

<body>
    <p class="description">
        ok 兄弟你每次點選“節點2” 就會發現這個參數的不同點了。是不是 它的觸發順序出現了不同 對 就是這樣的。
    </p>
    <label>
        <input checked onchange="radioChange(this.value)" name="useCapture" type="radio" id="radio2" value="false" />
        useCapture=false
    </label>
    <label>
        <input onchange="radioChange(this.value)" name="useCapture" type="radio" id="radio1" value="true" />
        useCapture=true
    </label>
    
    <div title="節點1">
        <span>節點1</span>
        <div title="節點2">
            <span>節點2</span>
        </div>
    </div>

    <script>
        //  個人總結:false的話 就是事件冒泡了 從子元素到父元素
        // true的話   就是事件捕獲 從父到子!
        let useCapture = false
        // 添加事件
        function bindEvent() {
            const targets = document.querySelectorAll('div')
            targets.forEach(element => {
                element.addEventListener('click', handleClick, useCapture)
            });
        }
        // 移除事件
        function removeEvent() {
            const targets = document.querySelectorAll('div')
            targets.forEach(element => {
                element.removeEventListener('click', handleClick, useCapture)
            });
        }
        // div click handle
        function handleClick(event){
            event.stopPropagation()
            alert(this.getAttribute('title'))
        }
        // 處理 radio change
        function radioChange(checked) {
            removeEvent()             
            console.log('radioChange')
            console.log(checked)
            useCapture = checked=="true" ? true : false
            bindEvent()
        }
        // 執行綁定事件
        bindEvent()
    </script>
</body>

</html>           

發現事件觸發了以後,就不再冒泡或者繼續往子元素傳遞了。這是我的了解和簡介歡迎大家拍磚。