关于DOM2级事件的事件捕获和事件冒泡

当浏览器开发到第四代的时候,浏览器的开发团队遇到了一个很有意思的问题:页面的哪一部分会拥有特定的事件?要明白这个问题问的是什么,我们可以想下面的一个问题,加入我们在一张纸上画了一组同心圆.如果把手放在纸上,那么手指指向的不是那一个特定的圆而是纸上所有的圆.两家浏览器开发团队在看待浏览器事件方面还是一致的.如果我们单击 某个按钮,他们都认为单击事件并不仅仅发上在按钮身上.换句话说,你也同时单击了按钮的容器元素,甚至也单击了整个页面.

事件流描述的就是从页面中接收事件的顺序.但是IENetspace 开发团队提出了差不多但是完全相反的事件流的概念.

  1. IE 的事件流是事件冒泡流
  2. Netspace 的事件流是事件捕获流

1.事件冒泡

IE 的事件流叫做事件冒泡流,即事件开始由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到不太具体的节点(文档)

事件默认是冒泡的,子元素触发事件时,会沿着DOM结构一层一层向上传递,这个过程称为事件冒泡。
注意:子元素被定位到了其他位置(视觉上脱离了父元素),也会见事件一层一层向上传递

看下面的例子:

在这里绑定事件用的是 DOM2级事件,它接收三个参数:

  1. 要处理的事件名
  2. 作为事件处理程序的函数
  3. 布尔值(true,表示在捕获阶段调用事件处理程序,false:表示在事件冒泡阶段调用事件处理程序)
 <style>
        * {
            margin:0;
            padding:0;
        }
        #orange{
            position: relative;
            width: 300px;
            height: 300px;
            margin:100px auto;
            background-color: orange;
        }

        #purple {
            position: absolute;
            top:0;
            left:0;
            right:0;
            bottom:0;
            margin:auto;
            width: 180px;
            height: 180px;
            background-color: purple;
        }

        #blue{
            position: absolute;
            top:0;
            left:0;
            right:0;
            bottom:0;
            margin:auto;
            width: 80px;
            height: 80px;
            background-color: #58a;
        }
    style>   
head>
<body>
    <div id="orange">
        <div id="purple">
            <div id="blue">div>
        div>
    div>
    <script>
       document.getElementById('orange').addEventListener('click', function(){
            console.log("我是橘色盒子");
        }, false);

        document.getElementById('purple').addEventListener('click', function(){
            console.log("我是紫色盒子");
        }, false);
        
        document.getElementById('blue').addEventListener('click', ()=>{
            console.log("我是蓝色盒子");
        }, false);
    script>

在页面中的显示如下图:
关于DOM2级事件的事件捕获和事件冒泡_第1张图片
当我点击最中心的蓝色盒子时会打印的信息如下:
关于DOM2级事件的事件捕获和事件冒泡_第2张图片
当我单击了页面中最中心的蓝色盒子时,click 事件会按照下面的顺序进行传播:

  1. div#blue
  2. div#purple
  3. div#orange
  4. body
  5. html
  6. doucument

也就是说 click 事件首先在最里面的蓝色盒子上发生,这个元素就是我们开始时单击的元素,然后 click 事件会沿着 DOM 树向上传播,直至传播到 document 对象:
如下图所示:
关于DOM2级事件的事件捕获和事件冒泡_第3张图片
所有的现代浏览器都支持事件冒泡,但在具体的实现上还是有一些差别,IE5.5及更早的版本中事件冒泡会跳过 元素(直接从 bodydocument).IE9 FireFox ChromeSafari 则将事件一直冒泡到 window 对象.

2. 事件捕获

Netspace 团队提出的另一种事件流叫做 事件捕获,事件捕获的思想是不太具体的节点应该更早的接收到事件,而最具体的节点应该最后接收到事件

事件默认是冒泡的,可通过 addEventListener 的第三个参数这只为 true 将事件设置为捕获,同时,添加了捕获的事件在使用 removeEventListener 移除时也需要写第三个参数 true

将上题中的代码事件监听中的第三个参数由 false 改为 true ,观察一下结果:

       document.getElementById('orange').addEventListener('click', function(){
           console.log("我是橘色盒子");
       }, true);

       document.getElementById('purple').addEventListener('click', function(){
           console.log("我是紫色盒子");
       }, true);

       document.getElementById('blue').addEventListener('click', ()=>{
           console.log("我是蓝色盒子");
       }, true);

点击盒子后打印的信息如下:
关于DOM2级事件的事件捕获和事件冒泡_第4张图片
我们单击了页面中的蓝色盒子,那么元素就会以下列顺序触发 click 事件

  1. document
  2. html
  3. body
  4. div#orange
  5. div#purple
  6. div#blue

在事件捕获过程中,document 对象首先接受到 click 事件,然后事件沿 dom树依次向下,一直传播到事件的实际目标,即 div#blue 元素,下图就展示了事件捕获的过程:
关于DOM2级事件的事件捕获和事件冒泡_第5张图片

3.DOM 事件流

DOM2级事件 规定的事件流包括三个阶段:

  1. 事件捕获阶段
  2. 处于目标阶段
  3. 事件冒泡阶段
    首先发生的是事件捕获,为截获事件提供了机会.然后是实际的目标接受到事件,最后一个阶段是事件冒泡阶段,这个阶段可以对事件作出相应,以上面的案例为例,点击最中间的 div#blue 盒子会按下图所示触发事件:
    关于DOM2级事件的事件捕获和事件冒泡_第6张图片
    DOM 事件流中,实际的目标元素(div#blue)在捕获阶段不会接收到事件.这意味着在捕获阶段事件从哪个 windowdocument 再到 htmlbody 一直到 div#purple 就停止了,下一个阶段是 “处于目标阶段” ,于是在事件 div#blue 上发生,然后冒泡阶段发生,事件传回文档.

4.阻止默认事件和事件冒泡

默认事件 浏览器中很多操作都有默认的行为,比如右键打开菜单,我们称之为默认事件。
我们可以通过 DOM 中的事件对象来阻止默认事件.
阻止默认事件:

  1. ev.stopDefault()
  2. return false

event 对象包含与创建它的特定事件有关的属性和方法,常见的如下:
阻止事件冒泡等.

属性/方法 类型 说明
preventDefault Function 取消事件的默认行为.
stopImmediate Propagation() Function 取消事件的进一步捕获或冒泡,同时阻止任何事件处理程序被调用
stopPropagation Function 取消事件的进一步捕获或冒泡.

将最开始的案例阻止事件冒泡,改写如下代码:

          document.getElementById('blue').addEventListener('click', (ev) => {
            console.log("我是蓝色盒子");
        }, false);

改为:

          document.getElementById('blue').addEventListener('click', (ev) => {
            ev.stopPropagation();
            console.log("我是蓝色盒子");
        }, false);

点击蓝色盒子后观察打印结果:
关于DOM2级事件的事件捕获和事件冒泡_第7张图片
已经成功阻止事件冒泡.
在平时写的时候,我通常会将默认事件和事件冒泡都阻止,防止产生不必要的麻烦.

            ev.stopPropagation();
            ev.preventDefault();

你可能感兴趣的:(JavaScript)