事件冒泡、事件委托、事件捕获等

139 阅读2分钟

1、事件冒泡

当触发子元素的事件时,该子元素的所有“父级元素” 的“同名事件”会依次触发

  • 事件冒泡现象一直都存在,只是以前没有给父元素注册同名事件
  • 子元素 → 父元素 → body → html → document → window
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style>
        .father {
            width: 200px;
            height: 200px;
            background-color: pink;
        }

        .son {
            width: 100px;
            height: 100px;
            background-color: skyblue;
        }
    </style>
</head>

<body>
    <div class="father">
        <div class="son"></div>
    </div>
    <script>
        let father = document.querySelector('.father')
        let son = document.querySelector('.son')

        //子元素
        son.onclick = function () {
            alert('我是son')
        }

        //父元素
        father.onclick = function () {
            alert('我是father')
        }

        //body
        document.body.onclick = function () {
            alert('我是body')
        }

        //html
        document.documentElement.onclick = function () {
            alert('我是html')
        }

        //document
        document.onclick = function () {
            alert('我是document')
        }

        //window
        window.onclick = function () {
            alert('我是window')
        }
    </script>
</body>

</html>

2、事件委托

给父元素注册事件,委托子元素来处理

  • 事件委托原理:事件冒泡
  • 事件委托注意点 : 不能使用 this
  • this: 事件源,指向父元素
  • e.target: 事件触发源,指向具体触发这个事件委托的子元素

需求:给每一个li元素注册点击事件,点击之后显示自己的文本

方式一:以前的方法,遍历数组逐一注册

<body>
    <ul>
        <li>扬帆起航1</li>
        <li>扬帆起航2</li>
        <li>扬帆起航3</li>
        <li>扬帆起航4</li>
        <li>扬帆起航5</li>
        <li>扬帆起航6</li>
    </ul>

    <script>
        let liList = document.querySelectorAll('ul>li');
        for (let i = 0; i < liList.length; i++) {
            liList[i].onclick = function () {
                alert(this.innerText)
            }
        }
    </script>
</body>

方式二:使用事件冒泡,给父元素注册

<body>
    <ul>
        <li>扬帆起航1</li>
        <li>扬帆起航2</li>
        <li>扬帆起航3</li>
        <li>扬帆起航4</li>
        <li>扬帆起航5</li>
        <li>扬帆起航6</li>
    </ul>

    <script>
        let ul = document.querySelector('ul')
        ul.onclick = function (e) {
            alert(e.target.innerText)
        }
    </script>
</body>

3、事件捕获

事件捕获,与事件冒泡完全相反

  • 会先从最顶级父元素 window一级一级往下触发
  • window -> document -> html -> body -> 父元素 -> 子元素
  • 默认情况下注册的事件都是冒泡类型事件
  • 只有唯一的一种语法可以注册捕获类型事件: 事件源.addEventListener('事件类型',事件处理函数,true) : 第三个参数为true
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style>
        .father {
            width: 200px;
            height: 200px;
            background-color: pink;
        }

        .son {
            width: 100px;
            height: 100px;
            background-color: skyblue;
        }
    </style>
</head>

<body>
    <div class="father">
        <div class="son"></div>
    </div>
    <script>
        let father = document.querySelector('.father')
        let son = document.querySelector('.son')

        //◆子元素
        son.addEventListener(
            'click',
            function () {
                alert('我是son')
            },
            true
        ) //捕获

        //◆父元素
        father.addEventListener(
            'click',
            function () {
                alert('我是father')
            },
            true
        )

        //◆body
        document.body.addEventListener(
            'click',
            function (e) {
                alert('我是body')
            },
            true
        )

        //◆html
        document.documentElement.addEventListener(
            'click',
            function () {
                alert('我是html')
            },
            true
        )

        //◆document
        document.addEventListener(
            'click',
            function () {
                alert('我是document')
            },
            true
        )

        //◆window
        window.addEventListener(
            'click',
            function () {
                alert('我是window')
            },
            true
        )
    </script>
</body>

</html>

4、事件流三个阶段 : e.eventPhase获取事件阶段

捕获阶段>目标阶段>冒泡阶段

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

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style>
        .father {
            width: 200px;
            height: 200px;
            background-color: pink;
        }

        .son {
            width: 100px;
            height: 100px;
            background-color: skyblue;
        }
    </style>
</head>

<body>
    <div class="father">
        <div class="son"></div>
    </div>
    <script>
        let father = document.querySelector('.father')
        let son = document.querySelector('.son')

        //◆子元素
        son.addEventListener(
            'click',
            function (e) {
                alert('我是son :' + e.eventPhase)
            },
            false
        ) //冒泡

        //◆父元素
        father.addEventListener(
            'click',
            function (e) {
                alert('我是father:' + e.eventPhase)
                e.stopPropagation()
            },
            false
        )//冒泡

        //◆body
        document.body.addEventListener(
            'click',
            function (e) {
                alert('我是body:' + e.eventPhase)
            },
            false
        )//冒泡

        //◆html
        document.documentElement.addEventListener(
            'click',
            function (e) {
                alert('我是html:' + e.eventPhase)
            },
            false
        ) //冒泡

        //◆document
        document.addEventListener(
            'click',
            function (e) {
                alert('我是document:' + e.eventPhase)
            },
            true
        ) //捕获

        //◆window
        window.addEventListener(
            'click',
            function (e) {
                alert('我是window :' + e.eventPhase)
            },
            true
        ) //捕获
    </script>
</body>

</html>
  1. 如果点击子元素:window(1)>document(1)>子元素(2)>父元素(3)
  2. 如果点击父元素:window(1)>document(1)>父元素(2)
  3. 如果点击body:window(1)>document(1)>html(2)

IE 浏览器只支持冒泡不支持捕获,W3C 浏览器先执行捕获,后执行冒泡

5、阻止事件流动: e.stopPropagation()

无论是冒泡还是捕获都可以阻止(停止事件流),但不会阻止默认行为(a标签、form表单)

W3C 的方法是 e.stopPropagation(),IE则是使用 e.cancelBuble=true

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

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <style>
        .father {
            width: 200px;
            height: 200px;
            background-color: pink;
        }

        .son {
            width: 100px;
            height: 100px;
            background-color: skyblue;
        }
    </style>
</head>

<body>
    <div class="father">
        <div class="son"></div>
    </div>
    <script>
        let father = document.querySelector('.father')
        let son = document.querySelector('.son')

        //◆子元素
        son.onclick = function (e) {
            e.stopPropagation()
            alert('我是son')
        }

        //◆父元素
        father.onclick = function (e) {
            e.stopPropagation()
            alert('我是father')
        }

        //◆body
        document.body.onclick = function (e) {
            e.stopPropagation()
            alert('我是body')
        }

        //◆html
        document.documentElement.onclick = function (e) {
            e.stopPropagation()
            alert('我是html')
        }

        //◆document
        document.onclick = function (e) {
            e.stopPropagation()
            alert('我是document')
        }

        //◆window
        window.onclick = function (e) {
            e.stopPropagation()
            alert('我是window')
        }
    </script>
</body>

</html>

点击子元素不会冒泡,点击父元素也不会冒泡......

6、阻止默认行为,e.preventDefault()

W3C 的方法是 e.preventDefault(),IE则是使用 e.returnValue=false

<body>
    <a id="txt" href="http://www.baidu.com">不想去百度</a>
</body>
<script>
    let a = document.getElementById('txt')
    a.onclick = function (e) {
        if (e.preventDefault) {
            e.preventDefault()
        } else {
            window.event.returnValue = false
        }
    }
</script>

以上代码优化

<body>
    <a id="txt" href="http://www.baidu.com">不想去百度</a>
</body>
<script>
    let a = document.getElementById('txt')
    a.onclick = function (e) {
        window.event ? window.event.returnValue = false : e.preventDefault()
    }
</script>