javascript事件委托详解_多个点击事件事件委托_卖菜的小白的博客-程序员宅基地

技术标签: 事件委派  js面试题  

事件委托:就是将子类的事件交给父类去执行。事件委托利用的是事件冒泡的机制。

一、事件委托的好处

1、减少内存消耗
<ul>
	<li>1</li>
	<li>2</li>
	<li>3</li>
	<li>4</li>
</ul>
正如上面的例子,如果我们需要给每一个li标签都设置点击事件,那么需要设置很多
个点击事件,导致内存消耗就很大。如果我们使用事件委托,将全部的事件都委托给
最外层的Ul上,那么只需要在ul上监听一个事件即可。
2、动态绑定事件
我们在开发中很多时候,都需要去异步请求数据,并且将数据展示到前端页面上,如
果我们给每一个标签元素都设置点击事件,那么就必须在每一次新增或者删除标签元
素时,都要将相关的事件进行解绑或者新增。如果使用事件委托,那么就不用在标签
删除的时候将相关事件解绑或者赋值,因为所有的事件都设置在外层的标签元素上。

二、基本实现

<body>

  <ul>
    <li>标签1</li>
    <li>标签2</li>
    <li>标签3</li>
    <li>标签4</li>
    <li>标签5</li>
  </ul>

  <script>
    document.getElementsByTagName("ul")[0].addEventListener("click", function(e) {
    
      var event = e || window.event //兼容性问题
      var target = event.target || event.srcElement  //兼容性问题
      if(target.nodeName.toLowerCase() === "li") {
    
        console.log("this  is tag of li:" + target.innerHTML)
      }
    })
  </script>
</body>
试想一种情况,如果我们只是需要所有标签中的class为liStyle的元素,我们可以使用target.matches()方法。如下所示。
<body>

  <ul>
    <li class="liStyle">标签1</li>
    <li>标签2</li>
    <li class="liStyle">标签3</li>
    <li>标签4</li>
    <li class="liStyle">标签5</li>
  </ul>

  <script>
    document.getElementsByTagName("ul")[0].addEventListener("click", function (e) {
    
      var event = e || window.event
      var target = event.target || event.srcElement
      if (target.matches("li.liStyle")) {
    
        console.log("大家好:" + target.innerHTML)
      }
    })
  </script>

</body>
target.matches()函数存在一些兼容性问题(ie8以及以下的版本不支持),所以可以使用polyfill来解决。
代码为:
if (!Element.prototype.matches) {
    
  Element.prototype.matches =
    Element.prototype.matchesSelector ||
    Element.prototype.mozMatchesSelector ||
    Element.prototype.msMatchesSelector ||
    Element.prototype.oMatchesSelector ||
    Element.prototype.webkitMatchesSelector ||
    function(s) {
    
      var matches = (this.document || this.ownerDocument).querySelectorAll(s),
        i = matches.length;
      while (--i >= 0 && matches.item(i) !== this) {
    }
      return i > -1;            
    };
}
加上matches兼容性设置,总体代码为

<body>

  <ul>
    <li class="liStyle">标签1</li>
    <li>标签2</li>
    <li class="liStyle">标签3</li>
    <li>标签4</li>
    <li class="liStyle">标签5</li>
  </ul>

  <script>
    if (!Element.prototype.matches) {
    
      Element.prototype.matches =
        Element.prototype.matchesSelector ||
        Element.prototype.mozMatchesSelector ||
        Element.prototype.msMatchesSelector ||
        Element.prototype.oMatchesSelector ||
        Element.prototype.webkitMatchesSelector ||
        function (s) {
    
          var matches = (this.document || this.ownerDocument).querySelectorAll(s),
            i = matches.length;
          while (--i >= 0 && matches.item(i) !== this) {
     }
          return i > -1;
        };
    }
    document.getElementsByTagName("ul")[0].addEventListener("click", function (e) {
    
      var event = e || window.event
      var target = event.target || event.srcElement
      if (target.matches("li.liStyle")) {
    
        console.log("大家好:" + target.innerHTML)
      }
    })
  </script>

</body>

三、代码封装

function eventDelegate (parentSelector, targetSelector, events, foo) {
    
  // 触发执行的函数
  function triFunction (e) {
    
    // 兼容性处理
    var event = e || window.event;
    var target = event.target || event.srcElement;
    // 处理 matches 的兼容性
    if (!Element.prototype.matches) {
    
      Element.prototype.matches =
        Element.prototype.matchesSelector ||
        Element.prototype.mozMatchesSelector ||
        Element.prototype.msMatchesSelector ||
        Element.prototype.oMatchesSelector ||
        Element.prototype.webkitMatchesSelector ||
        function(s) {
    
          var matches = (this.document || this.ownerDocument).querySelectorAll(s),
            i = matches.length;
          while (--i >= 0 && matches.item(i) !== this) {
    }
          return i > -1;            
        };
    }
    // 判断是否匹配到我们所需要的元素上
    if (target.matches(targetSelector)) {
    
      // 执行绑定的函数,注意 this
      foo.call(target, Array.prototype.slice.call(arguments));
    }
  }
  // 如果有多个事件的话需要全部一一绑定事件
  events.split('.').forEach(function (evt) {
    
    // 多个父层元素的话也需要一一绑定
    Array.prototype.slice.call(document.querySelectorAll(parentSelector)).forEach(function ($p) {
    
      $p.addEventListener(evt, triFunction);
    });
  });
}


这里一共需要四个参数,第一个参数是,需要实现代理的父级元素,第二个参数是:
用于过滤触发事件的选择器元素的后代,第三个参数是:一个或多个用空格分隔的事
件类型和可选的命名空间,如 click 或 keydown.click,第四个参数是:需要代
理事件响应的函数

四、局限性

像focus和blur不存在事件冒泡,不能使用事件委托。
mousemove,mouseout存在事件冒泡,但是只能通过定位去计算,性能消耗大,所以也不试用。
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_47450807/article/details/122646086

智能推荐

Cocos2dx游戏开发系列笔记18:《跑酷》游戏源码解析_普通网友的博客-程序员宅基地

懒骨头(http://blog.csdn.net/iamlazybone QQ:124774397 )再来看一个比较常见的跑酷游戏其实吧我觉得就是有人用《萝莉快跑》的例子改的不过肯定有新东西学习这是骨头见过最好的demo,不用修改一点直接能运行。先报环境 vs2012 和 cocos2dx 2.2 和 win8.1我知道出新版本了好多人整天盼着新版本

语音识别芯片的经典应用案例_编程大乐趣的博客-程序员宅基地

基于YQ5969的语音识别芯片实现本文设计的语音识别与控制系统,具有体积小、成本低、便于携带等优点。可以作为一个处理语音信号的通用硬件平台。语音识别芯片YQ5969的应用案例基于YQ5969语音识别芯片的空调遥控器设计与实现选定空调品牌对应的遥控器,搜索空调型号,然后长按对码按键,按照提示完成适配即可。适配成功后,即可以通过语音对空调进行开关机,温度等设置,确实非常方便。根本不用去烦恼再去寻找空调..._语音模块 rst、data、busy

华为升腾安装_ascend toolkit_AI视觉网奇的博客-程序员宅基地

ubuntu 18.04python3.7.5https://support.huaweicloud.com/ti-atc-A300_3000_3010/altasatc_16_002.html_ascend toolkit

Python中le-3怎么表述_知乎1800赞 | 用Python自动化办公能做到哪些有趣或有用的事情?..._weixin_39793553的博客-程序员宅基地

知友:陈廷聿(1800+ 赞同)利益相关:Python办公自动化课程的讲师我想介绍一下我是如何从每天工作8小时,进化成每天工作10分钟的。不涉及太多的技术细节,毕竟知乎是一个分(现)享(编)知(故)识(事)的地方0.先自我介绍一下:我不是程序员,大学学的也不是IT专业。我在一个主要业务是成品粮交易的企业工作,我的岗位的主要工作就是制作各类数据统计报表、台账、数据图表等等,反正就是各类日报..._python le3

ZZ给计算机专业大学生的建议_今年建议报考计算机专业吗_BillZhang2016的博客-程序员宅基地

[关注大学生]Joel Spolsky给计算机系学生的建议 document.title="[关注大学生]Joel Spolsky给计算机系学生的建议 - "+document.title虽然大概一两年前我还在夸夸其谈桌面应用程序是将来的潮流,大学生们现在还是偶尔向我请教职业发展的问题。所以我把我的建议写下来。以供学生们阅读,嘲笑,忽略。大多数锐气十足的学生从来不向前辈征求意见。在计算机_今年建议报考计算机专业吗

微信小程序授权登录写在app.js获取数据_阿巴的光之逆子的博客-程序员宅基地

小程序授权登录就不多说了,官方文档上都有,那么大家获取数据时,都会怎么存储数据呢?也许是使用App({ onLaunch() { // 展示本地存储能力 const logs = wx.getStorageSync('logs') || [] logs.unshift(Date.now()) wx.setStorageSync('logs', logs) }, globalData: { useri

随便推点

《Linux性能优化实战》笔记(24)—— 动态追踪 DTrace_dtrace_probe_Hehuyi_In的博客-程序员宅基地

上一节,我以 ksoftirqd CPU 使用率高的问题为例,带你一起学习了内核线程 CPU 使用率高时的分析方法。先简单回顾一下。当碰到内核线程的资源使用异常时,很多常用的进程级性能工具,并不能直接用到内核线程上。这时,我们就可以使用内核自带的 perf 来观察它们的行为,找出热点函数,进一步定位性能瓶颈。不过,perf 产生的汇总报告并不直观,所以我通常也推荐用火焰图来协助排查。其实,使用 perf 对系统内核线程进行分析时,内核线程依然还在正常运行中,所以这种方法也被称为动态追踪技术。_dtrace_probe

【问题】——source insight4工具栏错乱_ubuntu sourceinsight 工具栏显示问题_农夫山泉2号的博客-程序员宅基地

ubuntu16.04上采用wine安装source insight 4 之后,工具栏错乱,怎么破?_ubuntu sourceinsight 工具栏显示问题

Hash Join_hash join和哪几个guc参数相关_jinzhilong580231的博客-程序员宅基地

转自:http://blog.itpub.net/22861158/viewspace-664030/_hash join和哪几个guc参数相关

HighChart使用注意_洛阳纸贵的博客-程序员宅基地

1.柱状图上显示数据plotOptions: { column: { borderWidth: 0, dataLabels: { enabled: true //柱状图上显示数据 } }}2.去掉1,2位置//去除右下角highchart.com credits: { enabled:false }//去除右上角...

html自动全屏播放图片,视频播放HTML5时自动全屏切换(Auto Full Screen Toggle When Video Plays HTML5)..._weixin_39739234的博客-程序员宅基地

视频播放HTML5时自动全屏切换(Auto Full Screen Toggle When Video Plays HTML5)我有一个视频剪辑,当定时器在几分钟后得到滴答声时,它会在定时器中播放。我想要的是每当视频开始播放它应该自动进入全屏模式,当它停止时,然后回到位置,因为一旦视频停止我有计时器滴答。我知道这个requestFullScreen()方法,但我不知道如何在视频开始播放时调用它?任..._html网页视频全屏与图片技术

Linux中查看开启了哪些服务_指尖de柔情的博客-程序员宅基地

chkconfig --list每个运行级别下,服务的启动状态:运行级别0:系统停机状态运行级别1:单用户工作状态运行级别2:多用户状态(没有NFS)运行级别3:完全的多用户状态(有NFS),登陆后进入控制台命令行模式运行级别4:系统未使用,保留运行级别5:X11控制台,登陆后进入图形GUI模式运行级别6:系统正常关闭并重启...

推荐文章

热门文章

相关标签