资讯专栏INFORMATION COLUMN

关于JS中事件代理的解析

KavenFan / 2329人阅读

摘要:事件代理原理事件代理本质上来说是利用事件冒泡的机制来进行实现的。

概述

一般来说,我们在为前端页面设计交互的的时候往往需要为DOM元素添加事件处理程序。但是很多时候页面的DOM元素的结构和层级会很复杂,如果我们为所有需要添加事件处理的DOM元素一一绑定上事件处理程序,那么不仅编写出的代码会很繁杂,整个页面的性能也会很低下。比如我们有一个有序或者无序的列表,其中包裹了数百个子节点li,一般来说,在通过选择器拿到元素集合后,我们会用for循环对集合进行遍历,然后为其添加事件处理方法,这么做会带来什么结果呢,在JS引擎中,代码操作的DOM数量和添加的事件处理程序数量直接关系到页面的整体性能。操作的DOM数量越多,对DOM的访问次数越多,浏览器的重绘次数就越来越多,页面的响应速度和运行性能也就越来越差。所以,在进行程序优化的过程,其中一个重要的思路就是减少DOM的操作。

这时,事件代理存在的重要意义就体现出来了,它为我们提供了一种新的解决为大量类似的DOM元素添加事件处理的解决方案。只为一个容器或者说父节点添加一次事件处理程序,就达成了控制这个容器中一系列元素的目标,大大的减少了浏览器对DOM的访问。

事件代理原理

事件代理本质上来说是利用事件冒泡的机制来进行实现的。何为事件冒泡呢,百科中的解释是当事件发生后,这个事件就要开始传播(从里到外或者从外向里)。为什么要传播呢?因为事件源本身(可能)并没有处理事件的能力,即处理事件的函数(方法)并未绑定在该事件源上。例如我们点击一个按钮时,就会产生一个click事件,但这个按钮本身可能不能处理这个事件,事件必须从这个按钮传播出去,从而到达能够处理这个事件的代码中(例如我们给按钮的onclick属性赋一个函数的名字,就是让这个函数去处理该按钮的click事件),或者按钮的父级绑定有事件函数,当该点击事件发生在按钮上,按钮本身并无处理事件函数,则传播到父级去处理。

相关实现

具体怎么实现事件代理呢,我们来看一些简单的相关例子:

先写一个简单的有序列表结构。

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5

比如我们想要实现的是点击li,弹出li的序列。如果要直接操作li。

var lists = document.querySelectorAll("li");
for(let i = 0; i < lists.length; i ++){
    lists[i].onclick =function () {
        alert(lists[i].innerText);
    }
}

通过选择器拿到了li的集合,然后进行遍历并一一绑定点击事件,如果使用事件代理来实现呢

var lists = document.querySelector("ol");
lists.onclick = function (event) {
    alert(event.target.innerText);
}

通过选择器拿到li的容器也就是父节点ol,然后只绑定一次点击事件,并通过event对象提供的属性target指向容器的子节点,然后弹出该子节点的文本内容。需要了解的是,event对象提供了两个属性,分别是currentTarget和target,前者指向的是绑定事件的当前节点,后者则指向当前节点的一级子节点。当我们面临事件冒泡和事件捕获时,这两个属性能够为我们提供更高的灵活度。

上诉例子中,我们需要添加事件的DOM元素都是相同的,事件的种类也是一样的,如果我们面对的是结构更复杂一些的DOM集合呢,来看下面的例子。

DOM结构:

在这样的DOM结构中,我们要为四种不同的元素添加事件控制,如果只是通过target指向的话是无法实现的,需要挖掘target更深层的属性来进行条件判断,而target具备的属性就是指向的子节点所具备的属性,比如nodeName、id、className、value等

let box = document.querySelector(".wrap");
box.onclick = function (event) {
    if(event.target.nodeName === "button"){
        //...
    }else if(event.target.value === "提交"){
        //...
    }else if(event.target.className === "text"){
        //...
    }else{
        //...
    }
};

这样我们同样只在父节点上绑定一次事件就完成了我们想要的效果,无论是编码效率和性能损耗都比不适用事件代理的情况下更加优化。我们可以发现,相比与遍历元素集合的方式,事件代理最大的好处就是减少了DOM的操作,从来提升了效能。

适用场景

事件代理并非在所有场景中都适用的,在使用事件代理时,我们需要考虑添加事件处理的父节点能否触发我们想要绑定的事件,比如:


当我们想为容器中的文本框绑定blur事件改变框体颜色时

let box = document.querySelector(".wrap");
box.addEventListener("blur",function (event) {
    event.target.style.borderColor = "red";
});

我们发现这样做是无法生效的,应为div元素是无法触发onblur事件的,同时还有其他有关的输入框事件也是无法触发的,如oninput、onfocus等。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/100406.html

相关文章

  • 一文看透浏览器架构

    摘要:值得注意不同的浏览器使用不同的。今天截至年月,在浏览器进程中运行。这意味着浏览器进程包含一个实例,这是一个潜在的安全漏洞。 本文由云+社区发表作者:廖彩明 在从事前端开发过程中,浏览器作为最重要的开发环境,浏览器基础是是前端开发人员必须掌握的基础知识点,它贯穿着前端的整个网络体系。对浏览器原理的了解,决定着编写前端代码性能的上限。浏览器作为JS的运行环境,学习总结下现代浏览器的相关知识...

    sunsmell 评论0 收藏0
  • 【Step-By-Step】一周面试题深入解析 / 周刊 03

    摘要:禁止内联脚本执行规则较严格,目前发现使用。合理使用上报可以及时发现,利于尽快修复问题。因为事件会从目标元素一层层冒泡至对象。允许给一个事件注册多个监听。表示在捕获阶段触发,表示在冒泡阶段触发。 关于【Step-By-Step】 Step-By-Step (点击进入项目) 是我于 2019-05-20 开始的一个项目,每个工作日发布一道面试题。每个周末我会仔细阅读大家的答案,整理最一份...

    hedge_hog 评论0 收藏0
  • 前端面试之路二(javaScript基础整理)

    摘要:在标签中添加属性,本质上是跟在标签里面写属性时一样的,所以属性值最终都会编译为字符串类型。这个节点包括很多,比如,以及一些方法等方法。一个对象有很多,该集合名字为,里面有其他以及,里面有很多。 一、变量类型和计算 JS中使用typeof能得到哪些类型 变量类型 值类型:变量本身就是含有赋予给它的数值的,它的变量本身及保存的数据都存储在栈的内存块当中 引用类型:引用类型当然是分配到...

    AbnerMing 评论0 收藏0
  • 前端面试题目汇总

    摘要:线程在执行过程中与进程还是有区别的。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。 关于js 1.原型链 2.AJAX请求数据时解决缓存的办法3.js的继承 ...

    lastSeries 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<