深入解析:JS与Vue中事件委托(事件代理)的高效实现方法

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 深入解析:JS与Vue中事件委托(事件代理)的高效实现方法

一、事件委托(事件代理)

将原本需要绑定在子元素上的事件监听器委托在父元素上,让父元素充当事件监听的职务。


利用事件冒泡的特性,在父节点上响应事件,而不是在子节点上响应事件的技术。它能够改善性能,因为只需要在父元素上设置一次事件监听器,就可以管理同一类型的所有子元素的事件。


即事件从最深的节点开始,逐步向上传播。在事件冒泡过程中,父元素会捕获到子元素的事件,并进行分析。通过查看event对象的属性,可以确定是哪个子元素的事件,从而执行相应的处理逻辑。


使用事件委托能够避免对每个子元素单独设置事件监听器,降低了与DOM交互的次数,提高了页面的整体运行性能。同时,事件委托也具有更高的灵活性和可维护性,不需要操作大量的DOM元素。


在Vue中,可以利用v-on指令或@符号来绑定事件监听器,并在父元素上设置事件委托。例如,可以在父元素上设置一个click事件监听器,然后在子元素上绑定一个click事件,通过事件委托实现父元素对子元素事件的响应。

1、事件委托的优点
  • 节省内存(dom与js的关联),减少事件的注册
  • 增加子元素也无需再注册事件
2、事件委托的缺点

获取绑定的节点数据会相对麻烦一点

3、为什么要使用事件委托

在日常开发中,很经常我们会遇到个问题,就是在长列表数据较多的时候,而又需要对子元素注册一些事件(如onClick),就会造成比较大的内存开支,很耗费性能,也可能会造成页面卡顿等等;

所以可以通过在父元素上添加@click监听,而不是在子元素上注册事件;

如果数据量比较少,就可忽略不计;

此外,事件处理程序需要与 DOM 节点进行交互,访问 DOM 的次数越多,引起浏览器重绘和重排的次数也就越多,从而影响页面的性能。

4、事件委托实现原理

事件委托是利用事件的冒泡原理来实现的,大致可以分为三个步骤:

  1. 确定要添加事件元素的父级元素;
  2. 给父元素定义事件,监听子元素的冒泡事件;
  3. 使用 event.target 来定位触发事件冒泡的子元素。
5、DOM事件流

事件流描述的是从页面中接收事件的顺序。事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即DOM事件流。


比如:我们给页面中的一个div注册了单击事件,当你单击了div时,也就单击了body,单击了html,单击了document。



DOM 事件流会经历3个阶段:

  • 捕获阶段:事件从文档的根节点流向目标对象。

事件从最不精确的对象(document 对象)开始触发,然后到最精确(也可以在窗口级别捕获事件,不过必须由开发人员特别指定),与事件冒泡相反,事件会从最外层开始发生,直到最具体的元素。


  • 当前目标阶段:在目标对象上被触发。
  • 冒泡阶段:回溯到文档的根节点。

微软提出了名为事件冒泡的事件流。事件按照从最特定的事件目标到最不特定的事件目标(document对象)的顺序触发。

6、事件委托的实现方法
1、vue写法
1.1、html代码
<div id="app">
    <div id="event-agent" @click="eventAgent">
        <p v-for="(item, index) in list" :key="index" :data-name="item.name" :data-index="index">{{item.name}}</p>
    </div>
</div>

> 获取节点参数 (data-index、data-name),则在 $event.target.dataset  { index: 'xxx', name: 'xxx' } 中取值

1.2、js代码
data() {
    return { 
         list: [
             { id: 1, name: 'kmj1'},
             { id: 2, name: 'kmj2'},
             { id: 3, name: 'kmj3'},
             { id: 4, name: 'kmj4'}
         ]   
    }
},
methods: { 
    // 事件委托
    eventAgent(e) {
       const target = e.target; 
       console.log(target )
       // 注意 e.target.nodeName 的元素名是大写的
       if (target  && target.nodeName === "P") {
           const dataset = target .dataset;
           console.log('$event.target.dataset : ' dataset ); // $event.target.dataset :  { name: 'xxx', index: 'xxx' }
       }
    }
}
2、原生的写法其实也差不多:
2.1、html代码
<div id="event-agent">
    <p :data-name="Item1" :data-index="0">Item 1</p>
    <p :data-name="Item2" :data-index="1">Item 2</p>
    <p :data-name="Item3" :data-index="2">Item 3</p>
    <p :data-name="Item4" :data-index="3">Item 4</p>
    <p :data-name="Item5" :data-index="4">Item 5</p>
    <p :data-name="Item6" :data-index="5">Item 6</p>
</div>
2.2、js代码
1.document.getElementById( "event-agent").onclick = function(event){ 
    // 兼容Ie的写法
    event = event || window.event;
    var  target = event.target || event.srcElement;  
    // 注意 e.target.nodeName 的元素名是大写的
    if (target  && target.nodeName === "P") {
       const dataset = target .dataset;
       console.log('$event.target.dataset : ' dataset ); // $event.target.dataset :  { name: 'xxx', index: 'xxx' }
    }
}; 
 
// 也可以用这种方式,其实都差不多的:
// 冒泡阶段处理程序  
document.getElementById( "event-agent").addEventListener( "click", (e) => {},   false);
// 捕获阶段处理程序
document.getElementById( "event-agent").addEventListener( "click", (e) => {},   true);


二、阻止事件冒泡

W3C的方法是e.stopPropagation(),IE则是使用e.cancelBubble = true。 stopPropagation是事件的一个方法,作用是阻止目标元素的事件冒泡,但是不会组织默认行为。

 

function stopBubble(e) { 
//如果提供了事件对象,则这是一个非IE浏览器 
if ( e && e.stopPropagation ) 
    //因此它支持W3C的stopPropagation()方法 
    e.stopPropagation(); 
else 
    //否则,我们需要使用IE的方式来取消事件冒泡 
    window.event.cancelBubble = true; 
}


三、取消默认事件

W3C的方法是e.preventDefault(),IE则是使用e.returnValue = false。 preventDefault()是事件对象(Event)的一个方法,作用是取消一个目标元素的默认行为。既然是默认行为,那么元素必须有默认行为才能被取消,如果元素本身就没有默认行为,调用自然就无效了。什么元素有默认行为呢?如链接\<a href="">,提交按钮<input type=”submit”>等,这些都有默认行为。


 

function preventDefault(e){
    if(e && e.preventDefault){
        e.preventDefault();
    }
    else{
        window.event.returnValue = false;
    }
    return false;
}


四、总结

要使用事件委托,需要保证事件能够发生冒泡,适合使用事件委托的事件有 click、mousedown、mouseup、keydown、keyup、keypress 等。需要注意的是,虽然 mouseover 和 mouseout 事件也会发生事件冒泡,但处理起来非常麻烦,所以不推荐在 mouseover 和 mouseout 事件中使用事件委托。


另外,对于不会发生事件冒泡的事件(例如 load、unload、abort、focus、blur 等),则无法使用事件委托。


目录
相关文章
|
1天前
|
JavaScript API
深入解析JS中的visibilitychange事件:监听浏览器标签间切换的利器
深入解析JS中的visibilitychange事件:监听浏览器标签间切换的利器
8 0
|
1天前
|
JavaScript 前端开发 应用服务中间件
vue前端开发中,通过vue.config.js配置和nginx配置,实现多个入口文件的实现方法
vue前端开发中,通过vue.config.js配置和nginx配置,实现多个入口文件的实现方法
14 0
|
1天前
|
JavaScript 前端开发 大数据
在JavaScript中,Object.assign()方法或展开语法(...)来合并对象,Object.freeze()方法来冻结对象,防止对象被修改
在JavaScript中,Object.assign()方法或展开语法(...)来合并对象,Object.freeze()方法来冻结对象,防止对象被修改
5 0
|
1天前
|
存储 JavaScript 前端开发
Vue.js项目中全面解析定义全局变量的常用方法与技巧
Vue.js项目中全面解析定义全局变量的常用方法与技巧
4 0
|
6天前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
21 0
|
6天前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
19 0
|
6天前
|
存储 Java C++
Collection-PriorityQueue源码解析
Collection-PriorityQueue源码解析
15 0
|
6天前
|
安全 Java 程序员
Collection-Stack&Queue源码解析
Collection-Stack&Queue源码解析
17 0
|
4天前
|
存储
让星星⭐月亮告诉你,HashMap的put方法源码解析及其中两种会触发扩容的场景(足够详尽,有问题欢迎指正~)
`HashMap`的`put`方法通过调用`putVal`实现,主要涉及两个场景下的扩容操作:1. 初始化时,链表数组的初始容量设为16,阈值设为12;2. 当存储的元素个数超过阈值时,链表数组的容量和阈值均翻倍。`putVal`方法处理键值对的插入,包括链表和红黑树的转换,确保高效的数据存取。
22 5
|
5天前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)

推荐镜像

更多