您好, 欢迎来到 !    登录 | 注册 | | 设为首页 | 收藏本站

DOM 事件流

DOM 事件流描述了 DOM 时间响应的阶段、路径。

DOM 事件流也会被称为 DOM 事件模型。

事件流有三个阶段:

现代浏览器认都会在冒泡阶段触发事件。

通过例子来简单的感受一下。

<style>
  .@R_648_2@ {
    display: flex;
    align-items: center;
    justify-content: center;
  }

  .size-100 {
    width: px;
    height: px;
    background: #4caf50;
  }

  .size-200 {
    width: px;
    height: px;
    background: chocolate;
  }

  .size-300 {
    width: px;
    height: px;
    background: wheat;
  }
</style>

<div class="@R_648_2@ size-300">
  <div class="@R_648_2@ size-200">
    <div class="@R_648_2@ size-100">
    </div>
  </div>
</div>

<div class="result"></div>

<script>
  var @R_648_2@es = document.@H_222_@querySelectorAll('.@R_648_2@');
  var result = document.@H_222_@querySelector('.result');

  @R_648_2@es.@H_222_@forEach(function(@R_648_2@) {
    @R_648_2@.@H_222_@addEventListener('click', function() {
      var el = document.@H_222_@createElement('p');

      el.innerText = '现在触发点击事件的是' + this.className;

      result.@H_222_@appendChild(el);
    });
  });
</script>

点击后,观察可以发现,事件是点击到的最深层次的节点开始向上执行的。

即从 @H__30@size-100 到 @H__30@size-200 到 @H__30@size-300,这就是冒泡的过程。

如果想让事件在捕获阶段就执行,可以传递 @H__30@addEventListener 第三个参数。

addEventListener 的第三个参数用来决定事件在冒泡阶段触发还是在捕获阶段触发,其为布尔值,传递 @H__30@false 则事件会在冒泡阶段触发,传递 @H__30@true 则会在捕获阶段触发。

<style>
  .@R_648_2@ {
    display: flex;
    justify-content: center;
    align-items: center;
  }

  .ele1 {
    background: wheat;
    width: px;
    height: px;
  }

  .ele2 {
    background: yellowgreen;
    width: px;
    height: px;
  }
</style>

<div class="@R_648_2@ ele1">
  <div class="@R_648_2@ ele2"></div>
</div>

<div class="result"></div>

<script>
var ele1 = document.@H_222_@querySelector('.ele1');
var ele2 = document.@H_222_@querySelector('.ele2');
var result = document.@H_222_@querySelector('.result');

function @H_222_@getElement(content) {
  var el = document.@H_222_@createElement('p');

  el.innerText = content;

  return el;
}

ele1.@H_222_@addEventListener('click', function() {
  result.@H_222_@appendChild(@H_222_@getElement('我是元素ele1'));
});

ele2.@H_222_@addEventListener('click', function() {
  result.@H_222_@appendChild(@H_222_@getElement('我是元素ele2'));
});
</script>

根据认浏览器事件是在冒泡阶段触发的规则,上述例子会先触发子节点 @H__30@.ele2 的事件,再触发 @H__30@.ele1 的事件。

如果想让 @H__30@.ele1 在捕获阶段就触发事件,则在绑定事件的时候传递第三个参数为 @H__30@true 即可。

<style>
  .@R_648_2@ {
    display: flex;
    justify-content: center;
    align-items: center;
  }

  .ele1 {
    background: wheat;
    width: px;
    height: px;
  }

  .ele2 {
    background: yellowgreen;
    width: px;
    height: px;
  }
</style>

<div class="@R_648_2@ ele1">
  <div class="@R_648_2@ ele2"></div>
</div>

<div class="result"></div>

<script>
var ele1 = document.@H_222_@querySelector('.ele1');
var ele2 = document.@H_222_@querySelector('.ele2');
var result = document.@H_222_@querySelector('.result');

function @H_222_@getElement(content) {
  var el = document.@H_222_@createElement('p');

  el.innerText = content;

  return el;
}

ele1.@H_222_@addEventListener('click', function() {
  result.@H_222_@appendChild(@H_222_@getElement('我是元素ele1'));
}, true);

ele2.@H_222_@addEventListener('click', function() {
  result.@H_222_@appendChild(@H_222_@getElement('我是元素ele2'));
});
</script>

这样 @H__30@.ele1 的事件就会在捕获阶段触发。

早期的 IE 和 Navigator 是不符合标准的。

前者是使用事件冒泡流,后者使用事件捕获流。

前面的章节有提到过 @H__30@0级DOM事件 ,其提供的绑定事件的方式是不能指定事件触发的阶段的,其原因是在那个阶段下,还没有现在制定的 @H__30@DOM 事件流。

当时并没有统一的标准,@H__30@0级DOM事件也并不是一套官方出台的标准,所有相关内功全部由浏览器厂商决定。

后来 W3C 很好的整合了这两种模型,便有了现在的 DOM 事件流。

这个问题其实经常会在面试中被问到,通常题目会是这样的:

请描述一下事件捕获和冒泡的具体流程

其实问的是事件从那个节点开始捕获,然后到目标节点,最后又在哪个节点冒泡结束。

大部分面试者会回答 @H__30@document,其实根据事件对象的 @H__30@path 就可以得到答案。

path 会返回事件冒泡的路径,其最后是到 @H__30@window 对象才停止的。

其实这点在中也有描述。

注意:path 有兼容性问题,可以通过 can i use 确定。可以用标准中的 composedPath 代替。

开发过程中很少会取改变事件触发的阶段。但是事件流的概念依然重要,因为很多时候要阻止事件冒泡。

理解了事件流,可以理解事件委托的原理,事件委托相关的可以参阅事件相关的优化。


联系我
置顶