js|事件冒泡和事件委托🔥🔥

104 阅读2分钟

事件冒泡

1、概念

从实例的元素(事件)向上级父元素一级一级的执行上去,直到到达html

2、代码展示1

<body>
    <div id="div1">我是div1
        <div id="div2">我是div2
            <div id="div3">我是div3
                <div id="div4">我是div4</div>
            </div>
        </div>
    </div>
</body>
<script>
    let div1 = document.getElementById('div1');
    let div2 = document.getElementById('div2');
    let div3 = document.getElementById('div3');
    let div4 = document.getElementById('div4');
    div1.addEventListener('click', function () {
        alert('我是div1')
    })
    div2.addEventListener('click', function () {
        alert('我是div2')
    })
    div3.addEventListener('click', function () {
        alert('我是div3')
    })
    div4.addEventListener('click', function () {
        alert('我是div4')
    })
</script>

代码分析: 点击div1,弹出div1 点击div2,先弹出div2,然后弹出div1 点击div3,先弹出div3,再弹出div2,在弹出div1 点击div4,先弹出div4,在弹出div3,在弹出div2,在弹出div1 由于存在事件冒泡,所以会执行完自身之后,会执行上一级父元素的事件 addEventListener的第三个参数默认是false,默认是冒泡 addEventListener的第三个参数是true,是捕获

3、代码展示2

<body>
    <div id="div1">我是div1
        <div id="div2">我是div2
            <div id="div3">我是div3
                <div id="div4">我是div4</div>
            </div>
        </div>
    </div>
</body>
<script>
    let div1 = document.getElementById('div1');
    let div2 = document.getElementById('div2');
    let div3 = document.getElementById('div3');
    let div4 = document.getElementById('div4');
    div1.addEventListener('click', function () {
        alert('我是div1')
    },false)
    div2.addEventListener('click', function () {
        alert('我是div2')
    },true)
    div3.addEventListener('click', function () {
        alert('我是div3')
    },false)
    div4.addEventListener('click', function () {
        alert('我是div4')
    },true)
</script>

代码分析: 点击div1,div1, 点击div2,div2—>div1 点击div3,div2—>div3—>div1 点击div4,div2—>div4—>div3—>div1 当点击事件之前,父级存在捕获,是先执行捕获,在执行冒泡的

4、代码展示3

<body>
    <div id="div1">我是div1
        <div id="div2">我是div2
            <div id="div3">我是div3
                <div id="div4">我是div4</div>
            </div>
        </div>
    </div>
</body>
<script>
    let div1 = document.getElementById('div1');
    let div2 = document.getElementById('div2');
    div1.addEventListener('click', function () {
        alert('我是div1')
    },false)
    div2.addEventListener('click', function () {
        alert('我是div2捕获')
    },true)
    div2.addEventListener('click', function () {
        alert('我是div2冒泡')
    },false)
</script>

代码分析: div2捕获—>div2冒泡—>div1

5、代码展示4

<body>
    <div id="div1">我是div1
        <div id="div2">我是div2
            <div id="div3">我是div3
                <div id="div4">我是div4</div>
            </div>
        </div>
    </div>
</body>
<script>
    let div1 = document.getElementById('div1');
    let div2 = document.getElementById('div2');
    div1.addEventListener("click", function () {
        alert("div1");
    }, false);
    div2.addEventListener("click", function () {
        alert("div2_冒泡");
    }, false);
    div2.addEventListener("click", function () {
        alert("div2_捕获");
    }, true);
</script>

代码分析: div2捕获—>div2冒泡—>div1

6、代码展示5

<body>
    <div id="div1" class="div1">
        <div id="div2" class="div2"></div>
    </div>
</body>
<script>
    var div1 = document.getElementById('div1'); //父级
    var div2 = document.getElementById('div2');
    div2.onclick = function () {
        alert(2);
    }
    div1.onclick = function () {
        alert(1);
    }
</script>

当我点击div2的时候,弹出了2,然后又弹出了1,所以这里发生了事件冒泡,div2的点击事件传递给了父级div1,div1正好又绑定了事件触发的函数,所以会出现以上的效果 这里有一个比较迷惑的点,可能认为div2的位置在div1里面,点击了div2也就点击了div1,其实通过测试(调整div1和div2的位置是上下布局,发现冒泡还会存在)

20211011101616.gif

7、父级的事件触发函数没有操作

<body>
    <div id="div1" class="div1">
        <div id="div2" class="div2"></div>
    </div>
</body>
<script>
    var div1 = document.getElementById('div1'); //父级
    var div2 = document.getElementById('div2');
    div2.onclick = function () {
        alert(2);
    }
    div1.onclick = function () {

    }
</script>

20211011101957.gif

8、需求:点击div1展示div2 ,点击其他地方隐藏div2

<body>
    <div id="div1" class="div1">div1</div>
    <div id="div2" class="div2">div2</div>
</body>
<script>
    var div1 = document.getElementById('div1');
    var div2 = document.getElementById('div2');
    div1.onclick = function () {
      div2.style.display = 'block';
    }
    document.onclick = function(){
        div2.style.display = 'none'
    }
</script>

20211011102836.gif

上述的代码并没有按照预期的想法展示,因为发生了事件冒泡,因为div1执行onclick的时候,他会触发父级的点击事件然后一层一层上传,所以document的事件也被触发了,然后执行绑定的事件函数,div2消失了。。所以当点击div1的时候首先,让div2面板显示,只是事件执行太快了,很快又执行了document的点击事件,让面板隐藏。

9、取消事件冒泡

<body>
    <div id="div1" class="div1">div1</div>
    <div id="div2" class="div2">div2</div>
</body>
<script>
    var div1 = document.getElementById('div1');
    var div2 = document.getElementById('div2');
    div1.onclick = function (ev) {
        div2.style.display = 'block';
        stopBubble(ev); //停止div1的事件冒泡
    }
    document.onclick = function () {
        div2.style.display = 'none'
    }
    function stopBubble(e) {
        if (e && e.stopPropagation) {
            e.stopPropagation(); // w3c标准
        } else {
            window.event.cancelBubble = true; // IE的方式
        }
    };
</script>

20211011103412.gif

事件委托

<!DOCTYPE html>
<html lang="en">

<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>
</head>

<body>
    <ul id="ul">
        <li>111111</li>
        <li>222222</li>
        <li>333333</li>
    </ul>
    <button id="button">点击</button>
</body>
<script>
    window.onload = function () {
        let aul = document.getElementById('ul');
        let ali = document.getElementsByTagName('li');
        let button = document.getElementById('button');
        // 3个小li
        let num = 3;
        // 事件源 :event对象
        // ie:window.event.srcElement
        // 标准:event.target
        aul.onmousemove = function (e) {
            let ev = e || window.event;
            let target = ev.target || ev.srcElement;
            if (target.nodeName.toLowerCase() == 'li') {
                target.style.background = 'red';
            }
        }
        aul.onmouseout = function (e) {
            let ev = window.event || e;
            let target = ev.target || ev.srcElement;
            if (target.nodeName.toLowerCase() == 'li') {
                target.style.background = ''
            }
        }
        // 实现按钮的监听方法1
        // button.addEventListener('click', function () {
        //     num++;
        //     let lii = document.createElement('li');
        //     lii.innerHTML = 111111 * num;
        //     aul.appendChild(lii);
        // })
        // 实现按钮的监听方法2
        button.onclick = function () {
            num++;
            let lii = document.createElement('li');
            lii.innerHTML = 111111 * num;
            aul.appendChild(lii);
        }
    }
</script>

</html>

3.gif