DOM事件流

先来看一个例子,看下面这个代码

<div id="div1" onclick="test()">
    <div id="div2" onclick="test()">
        <div id="div3" onclick="test()">
        </div>
    </div>
</div>

三个嵌套的<div>每一个都绑定了一个点击事件

那么当我们点击最内层的<div>时,是不是也相当于点击了中层和外层的<div>呢?

似乎从理论是来说是这样的,如果真的是这样那外层的点击事件会不会被触发呢?

让我们来试一下

function test(){
    console.log("test");
}

经测试发现点击最内层的<div>时事件被触发了三次

不如我们干脆更细致一点

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>Page Title</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style>
    #div1 {
      width: 200px;
      height: 200px;
      border: 1px solid red;
    }

    #div2 {
      width: 100px;
      height: 100px;
      border: 1px solid green;
    }

    #div3 {
      width: 50px;
      height: 50px;
      border: 1px solid blue;
    }
  </style>
</head>

<body>
  <div id="div1" onclick="test1()">
    <div id="div2" onclick="test2()">
      <div id="div3" onclick="test3()">

      </div>
    </div>
  </div>



  <script>
    function test1() {
      console.log("1");
    }
    function test2() {
        console.log("2");
    }
    function test3() {
        console.log("3");
    }
  </script>
</body>

</html>

这段代码里我们给不同的<div>设置了不同的函数,打印出不同的数字

再次尝试点击最内层之后我们发现控制台按顺序打出了’3’ ‘2’ ‘1’

这说明从我们所点击的最内层开始,向外扩散,每一层的点击事件都被触发了,而且是由内向外的顺序。

而我们点击中间一层,打出了’2’ ‘1’
这似乎也很好理解,毕竟从视觉上我们也能看出来应该是这样。

那么我们换一种方式

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>Page Title</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style>
    #div1 {
      width: 200px;
      height: 200px;
      border: 1px solid red;
    }

    #div2 {
      width: 100px;
      height: 100px;
      border: 1px solid green;
    }

    #div3 {
      width: 50px;
      height: 50px;
      border: 1px solid blue;
    }
  </style>
</head>

<body>
  <div id="div1" >
    <div id="div2" >
      <div id="div3" >

      </div>
    </div>
  </div>



  <script>
    function test1() {
      console.log("1");
    }
    function test2() {
        console.log("2");
    }
    function test3() {
        console.log("3");
    }
    var oDiv=document.getElementsByTagName("div");
    oDiv[0].addEventListener("click",test1,true);
    oDiv[1].addEventListener("click",test2,true);
    oDiv[2].addEventListener("click",test3,true);
  </script>
</body>

</html>

先来解释一下做出的变动,这此我们采用的addEventListener的方式来绑定事件,第一个参数是事件名称,比如’click’,‘mouseover’,第二个参数是函数名,即所要绑定的函数,而第三个参数就比较特殊,我们稍后再说。

现在让我们点击最内层的<div>,不可思议的事情发生了
这次打印出的是 ‘1’ ‘2’ ‘3’;

按照之前的经验应该由内向外,但这次却是由外向内!

没错,这就牵扯到了我们所要说的事件流!

实际上无论我们是否绑定事件,每一个动作都会在对应的元素上触发,即每一个元素实际上都无时无刻不在触发着事件。只不过这些事件因为没有绑定函数,所以不会产生效果罢了。

以点击为例,当我们点击一个元素的时候,这个元素就会产生一个点击事件,这个点击事件并不是直接产生在这个元素上并直接作用的,而要“流”。

事件流主要分为两方面,一种是事件捕获,一种是事件冒泡。

事件冒泡:顾名思义,就是像冒泡泡一样不断的向上冒,在这里就是从产生事件的元素一层层的向父级元素扩散,直到扩散到html或者window对象。

事件捕获:刚好相反,当某个元素产生一个事件时,并不是直接在这个元素上产生,而是从最外层的window或者html开始产生一个事件,一层一层的向内层传递,直到该元素为止。

这两种不同的方式的产生和发展这里就不作讨论了。
当前的主流浏览器都是同时支持事件捕获和事件冒泡的。

我们说当前主流浏览器两者都支持,他们将这两者结合到一起,当产生一个事件时,先发生事件捕获,即事件捕获阶段,事件捕获到产生事件的具体元素上,开始判断有无事件句柄(绑定函数),即处于目标阶段,然后事件由该元素开始进行事件冒泡,即事件冒泡阶段。这三个阶段整合在一起就是事件流。
(实际上事件在每个节点都会判断事件句柄)

现在回过头来看前面所提到的addEventListener的第三个参数,实际上就是控制绑定函数在哪个阶段执行的,这个参数有两个选择,默认为false ,即冒泡阶段执行,如果设置为true则捕获阶段执行。

把上面的js代码改一下

  <script>
    function test1() {
      console.log("1");
    }
    function test2() {
        console.log("2");
    }
    function test3() {
        console.log("3");
    }
    var oDiv=document.getElementsByTagName("div");
    oDiv[0].addEventListener("click",test1,true);
    oDiv[1].addEventListener("click",test2,true);
    oDiv[2].addEventListener("click",test3,true);
    oDiv[0].addEventListener("click",test1,false);
    oDiv[1].addEventListener("click",test2,false);
    oDiv[2].addEventListener("click",test3,false);
  </script>

这时候我们点击最内层的<div>打印出的是’1’ ‘2’ ‘3’ ‘3’ ‘2’ ‘1’

由此我们也可很清楚的看到,先进行事件捕获,设置为true的在捕获阶段便执行函数,之后是事件冒泡,设置为false的冒泡阶段执行,为了更直观你也可以写成这样

  <script>
    function test1() {
      console.log("外层捕获");
    }
    function test2() {
        console.log("中层捕获");
    }
    function test3() {
        console.log("内层捕获");
    }
    function test4() {
        console.log("外层冒泡");
    }
    function test5() {
        console.log("中层冒泡");
    }
    function test6() {
        console.log("内层冒泡");
    }
    var oDiv=document.getElementsByTagName("div");
    oDiv[0].addEventListener("click",test1,true);
    oDiv[1].addEventListener("click",test2,true);
    oDiv[2].addEventListener("click",test3,true);
    oDiv[0].addEventListener("click",test4,false);
    oDiv[1].addEventListener("click",test5,false);
    oDiv[2].addEventListener("click",test6,false);
  </script>

就可以很清楚的看到事件的传递顺序了


合理运用事件冒泡可以实现事件委托,极大的节省资源。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值