DOM事件与事件委托

·  阅读 500

DOM事件是用户或者浏览器自己执行的某种动作,是文档或者浏览器发生的一些交互瞬间,比如点击(click)按钮等,这里的click就是事件名称

1.事件流

当我们在页面上单击一个按钮,页面上哪些元素会触发这个事件,是发生在这个元素上,还是这个元素的父元素也会触发这个点击事件呢?

这就牵扯到事件流。事件捕获事件冒泡分别由微软和网景公司提出,这两个概念都是为了解决页面中事件流(事件发生顺序)的问题。

1.png

2.事件捕获

网景提出了事件流名为事件捕获(event capturing)。事件会从外到内的开始发生,直到最具体的元素。

因此在事件捕获的概念下在td元素上发生click事件的顺序应该是

window -> document -> html -> body -> table -> tbody -> tr -> td
复制代码

3.事件冒泡

微软提出了名为事件冒泡(event bubbling)的事件流。事件冒泡可以形象地比喻为把一颗石头投入水中,泡泡会一直从水底冒出水面。也就是说,事件会从内到外的开始发生,一直向上传播,直到根对象。

因此在事件冒泡的概念下在td元素上发生click事件的顺序应该是

td -> tr -> tbody -> table -> body -> html -> document -> window
复制代码

在微软和网景打得火热之时,W3C作为和事老采用折中的方式,制定了统一的标准——先捕获再冒泡

addEventListener的第三个参数就是为冒泡和捕获准备的

element.addEventListener(event, function, useCapture)

//第一个参数是需要绑定的事件
//第二个参数是触发事件后要执行的函数
//第三个参数表示要用那种事件流,默认为false,表示在事件冒泡阶段调用事件处理函数,true则为在捕获阶段调用事件处理函数
复制代码

当事件捕获和事件冒泡一起存在的情况,事件又是如何触发呢?

把被点击的DOM节点为target节点总结就是:

  • 对于非target节点则先执行捕获在执行冒泡
  • 对于target节点则是先执行先注册的事件,无论冒泡还是捕获

4.事件委托

由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。这种方法叫做事件委托

事件委托的优点

  1. 可以大量节省内存占用,减少事件注册,比如在ul上代理所有li的click事件就非常棒
<ul id="list">
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
 ......
<li>item n</li>
</ul>
复制代码

如上面代码所示,如果给每个li列表项都绑定一个函数,那对内存的消耗是非常大的,因此较好的解决办法就是将li元素的点击事件绑定到它的父元素ul身上,执行事件的时候再去匹配判断目标元素

  1. 可以实现当新增子对象时无需再次对其绑定(动态绑定事件)
<ul id="test">
  <li id="li1">item 1</li>
  <li id="li2">item 2</li>
  <li id="li3">item 3</li>
</ul>
复制代码

上述代码按照传统的做法,需要像下面这样为它们添加 3 个事件处理程序

let item1 = document.getElementById("li1");
let item2 = document.getElementById("li2");
let item3 = document.getElementById("li3");
 
item1.onclick = function() {
    location.href = "http://www.baidu.com";
};
item2.onclick = function() {
    document.title = "事件委托";
};
item3.onclick = function() {
    alert("hi");
};
复制代码

如果在一个复杂的 Web 应用程序中,对所有可单击的元素都采用这种方式,那么结果就会有数不清的代码用于添加事件处理程序。此时,可以利用事件委托技术解决这个问题。

let item1 = document.getElementById("li1");
let item2 = document.getElementById("li2");
let item3 = document.getElementById("li3");
 
document.addEventListener("click", function (e) {
    let t = e.target;
    switch (t.id) {
     case "li1":
           document.title = "事件委托";
           break;
     case "li2":
           location.href = "http://www.baidu.com";
           break;
      case "li3": 
           alert("hi");
           break;
      }
    })
复制代码

使用“事件委托”时,并不是说把事件委托给的元素越靠近顶层就越好。事件冒泡的过程也需要耗时,越靠近顶层,事件的”事件传播链”越长,也就越耗时。如果DOM嵌套结构很深,事件冒泡通过大量祖先元素会导致性能损失。

分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改