DOM 事件机制

682 阅读3分钟

事件捕获

事件捕获模式流程:事件发生时,触发目标元素的祖先元素的事件响应函数,然后触发其父元素的事件响应函数,并逐级到目标元素

就是从外到内找监听函数,叫事件捕获

事件冒泡

事件冒泡模式流程正好与捕获模式相反,事件发生时,先触发目标元素的事件响应函数,再逐级追溯到其祖先元素

内到外找监听函数,叫事件冒泡。

DOM 事件流

DOM 事件流分为三个阶段:

  1. 事件捕获阶段 - 事件流由上往下直到目标元素
  2. 目标阶段 - 事件到达目标元素
  3. 事件冒泡阶段 - 事件流由下往上从目标元素直到最外层祖先元素

一般我们使用冒泡阶段的事件响应, addEventListener(event,handel) ,其实addEventListener还有第三个参数,如果需要指定捕获阶段的事件响应,设置第三个参数为true。

下面一个例子展示DOM事件流:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>DOM事件流</title>
</head>
<body>
    <div id="div1" style="border:1px solid #000;padding: 30px">
        DIV1
        <div id="div2"  style="border:1px solid #000;padding: 30px">
            DIV2
            <div id="div3"  style="border:1px solid #000; padding: 30px">DIV3</div>
        </div>
    </div>
</body>
<script type="text/javascript">
    var div1 = document.getElementById('div1');
    var div2 = document.getElementById('div2');
    var div3 = document.getElementById('div3');
    // true表示在捕获阶段处理事件、false表示在冒泡阶段处理
    div1.addEventListener('click',function (event) {
        console.log('事件捕获阶段: div1点击事件');
    }, true);
    div1.addEventListener('click',function () {
        console.log('事件冒泡阶段: div1点击事件');
    }, false);
    div2.addEventListener('click',function () {
        console.log('事件捕获阶段: div2点击事件');
    }, true);
    div2.addEventListener('click',function () {
        console.log('事件冒泡阶段: div2点击事件');
    }, false)
    div3.addEventListener('click',function () {
        console.log('事件捕获阶段: div3点击事件');
    }, true);
    div3.addEventListener('click',function () {
        console.log('事件冒泡阶段: div3点击事件');
    }, false)
</script>
</html>

在浏览器里执行,点击DIV3,可以看到控制台的输出:

一个特例

只有一个 div 被监听(不考虑父子同时被监听),fn 分别在捕获阶段和冒泡阶段监听click事件,用户点击的元素就是开发者监听的。

div.addEventListener('click',f1)
div.addEventListener('click',f2,true)

====> f1 先执行还是f2 先执行?

答:谁先监听谁先执行。

事件目标元素

e.targetthis/e.currentTarget 的区别

e.target------目标元素,最直接引起事件的元素 / 用户操作的元素

this/e.currentTarget ----当前事件响应函数绑定的元素 / 开发者设置监听的元素

取消冒泡

捕获不可取消,但冒泡可以

e.stopPropagation() 可中断冒泡

有些事件也不可取消冒泡

具体MDN,如 MDN 搜索 sroll event,看到Bubbles和Cancelable

  • Bubbles 表示事件是否冒泡
  • Cancelable 表示是否可以取消冒泡

阻止滚动

scroll 事件不可取消冒泡

阻止 scroll 默认动作没有用,因为先有滚动才有滚动事件。

要阻止滚动,可以阻止 wheel 和 touch 的默认动作。

注意要找准滚动条所在元素

滚动条还能用,可用CSS设置滚动条width:0

::webkit-scroll {width:0 !important}

使用CSS设置 overflow:hidden 可以直接取消滚动条,但此时 JS 依旧可以修改scrollTop

自定义事件

示例