最近在研究react的事件,都说他是合成事件,整个dom唯一绑定事件的是document。
我想这不就是事件委托么,有啥好研究的,于是我去重温了下事件委托,顺便看了阻止冒泡。

突然突发奇想,如果两个结合在一起呢?

能否实现子节点点击了,父节点不能触发点击事件

好像n年前我还是小萌新,在广电商做切图仔的时候请教过我的组长,他没有给到我满意的答案。今天这个想法又冒出来了。
我得解决它一下。

先看看react是怎么解决的

它用了 e.nativeEvent.stopImmediatePropagation();
nativeEvent.stopImmediatePropagation是react自己新增的属性方法。

那我的想法就来了。我也可以啊。

html

  <div class="a"><div class="b"><div class="e">点击<b>捣蛋鬼E</b>,会执行<B>e,b,a</B>函数</div><div class="c">点击<b>捣蛋鬼C,stop</b>,执行执行<b>c</b>函数</div><div class="f">点击<b>捣蛋鬼F</b>,会执行<b>f,b,a</b>函数</div></div></div>

javascript:
实现一个对象

  • 内部维护点击事件的数组
  • 可以通过构造函数传参的方式绑定委托事件的dom,如果不传默认是body
  • 动态给事件回调函数的e对象添加_stop=true来阻止冒泡
function ProxyClickEvent(proxyBox) {var self = this;self.eventPool = [];if (proxyBox) {self._proxyBox = proxyBox;} else {self._proxyBox = document.body;}self.__bindEvent = self._bindEvent.bind(self);self._proxyBox.addEventListener("click", self.__bindEvent)}ProxyClickEvent.prototype._bindEvent = function(e) {let self = this;let target = e.target;for (let i = self.eventPool.length - 1; i >= 0; i--) {let event = self.eventPool[i];let fn = event.fn;/*** 1.如果target等于事件对应的dom* 2.否则事件dom是target的父节点&&e._target不为false* 3.如果e._stop为true时候,break* 那就执行回调函数* * **/if (event.dom == e.target) {fn(e);} else if (!e.__stop && event.dom.contains(e.target)) {fn(e);} else if (e.__stop) {break;}}}ProxyClickEvent.prototype.add = function(dom, fn) {if (!(dom instanceof HTMLElement)) {console.error("invalid parameter dom", dom, "fn", fn)throw "function add arg1 should be a HTMLElement,but get " + (typeof dom)}if (typeof fn !== "function") {console.error("invalid parameter dom", dom, "fn", fn)throw "function add arg2 should be a function,but get " + (typeof fn)}this.eventPool.push({dom: dom,fn: fn})}ProxyClickEvent.prototype.remove = function(dom, fn) {this.eventPool = this.eventPool.filter(item => item.dom != dom && item.fn != fn)}ProxyClickEvent.prototype.destroyed = function() {this.eventPool = [];self._proxyBox.removeEventListener("click", this.__bindEvent);}

使用方式

 let proxyEvent = new ProxyClickEvent();window.onload = function() {proxyEvent.add(document.querySelector(".a"), function() {console.log("点击了a")})proxyEvent.add(document.querySelector(".b"), function() {console.log("点击了b")})proxyEvent.add(document.querySelector(".c"), function(e) {e.__stop = true;console.log("点击了c")})proxyEvent.add(document.querySelector(".e"), function() {console.log("点击了e")})proxyEvent.add(document.querySelector(".f"), function() {console.log("点击了f")})}

这存在在个问题①

必须得从父级到子节点,按顺序add,否则事件“冒泡顺序”会错乱

目前先假设使用场景场景:
用户可以自己明确且可以控制从父级到自己按顺序的add

例如以下场景就非常适用:

通过dom自定义属性clickFn来绑定函数名字,然后获取携带clickFn属性的dom,遍历后追加点击事件

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head><body><div class="a" clickFn="fna"><div class="b" clickFn="fnb"><div class="e" clickFn="fne">点击<b>捣蛋鬼E</b>,会执行<B>e,b,a</B>函数</div><div class="c" clickFn="fnc">点击<b>捣蛋鬼C,stop</b>,执行执行<b>c</b>函数</div><div class="f" clickFn="fnf">点击<b>捣蛋鬼F</b>,会执行<b>f,b,a</b>函数</div></div></div><script src="./ProxyClickEvent.js"></script><script>var funs = {fna: function() {console.log("点击a")},fnb: function() {console.log("点击b")},fnc: function(e) {e.__stop = true;console.log("点击c")},fnd: function() {console.log("点击d")},fne: function() {console.log("点击e")},fnf: function() {console.log("点击f")}}window.onload = function() {let proxyEvent = new ProxyClickEvent();let clickDoms = document.querySelectorAll("[clickFn]");for (let i = 0; i < clickDoms.length; i++) {let dom = clickDoms[i];let fnStr = clickDoms[i].getAttribute("clickFn");proxyEvent.add(dom, funs[fnStr])}}</script>
</body></html>

存在问题②

某个元素删除后,点击时候回调函数依旧会触发

解决方案

  1. 暴力定时检测更新dom是否存在,过滤掉数组里面dom不存在的元素,
  2. 并且提供函数destroy时候销毁定时器
  3. 因为定时任务只是更新数组变量不涉及费时操作,所以不需要当心性能问题
  4. 如果是单页面的话,必须在页面销毁前调用destroy函数,销毁定时器

建议:如果涉及到dom的父子节点的增删的话,必须清空数组,然后再遍历添加到数组

代码:

function ProxyClickEvent(proxyBox) {var self = this;self.eventPool = [];if (proxyBox) {self._proxyBox = proxyBox;} else {self._proxyBox = document.body;}//更新数组,过滤掉被销毁的dom的事件self._bind_clearRemoveDomEventItem = self._clearRemoveDomEventItem.bind(self);//定时过滤dom不存在的元素self._timer = setInterval(function() {self._bind_clearRemoveDomEventItem();}, 1000)self.__bindEvent = self._bindEvent.bind(self);self._proxyBox.addEventListener("click", self.__bindEvent)
}
ProxyClickEvent.prototype._clearRemoveDomEventItem = function() {if (this.eventPool.some((el) => !document.body.contains(el.dom))) {this.eventPool = this.eventPool.filter(item => document.body.contains(item.dom));}
}
ProxyClickEvent.prototype._bindEvent = function(e) {let self = this;let target = e.target;for (let i = self.eventPool.length - 1; i >= 0; i--) {let event = self.eventPool[i];let fn = event.fn;/*** 1.如果target等于事件对应的dom* 2.否则事件dom是target的父节点&&e._target不为false* 3.如果e._stop为true时候,break* 那就执行回调函数* * **/if (event.dom == target) {fn(e);} else if (!e.__stop && event.dom.contains(target)) {fn(e);} else if (e.__stop) {break;}}
}
ProxyClickEvent.prototype.add = function(dom, fn) {if (!(dom instanceof HTMLElement)) {console.error("invalid parameter dom", dom, "fn", fn)throw "function add arg1 should be a HTMLElement,but get " + (typeof dom)}if (typeof fn !== "function") {console.error("invalid parameter dom", dom, "fn", fn)throw "function add arg2 should be a function,but get " + (typeof fn)}this.eventPool.push({dom: dom,fn: fn})
}
ProxyClickEvent.prototype.remove = function(dom, fn) {this.eventPool = this.eventPool.filter(item => item.dom != dom && item.fn != fn)
}
ProxyClickEvent.prototype.destroy = function() {this.eventPool = [];this._proxyBox.removeEventListener("click", this.__bindEvent);clearInterval(this._timer);
}

已封装成npm包,点击去查看

点击事件 事件委托的情况下实现阻止冒泡相关推荐

  1. JS事件委托(什么情况下使用事件委托)

    1. 什么是事件委托 事件委托:把事情委托给别人,代为处理. 事件委托也称为事件代理,在 jQuery 里面称为事件委派. 理解:说白了就是,不给子元素注册事件,给父元素注册事件,把处理代码在父元素的 ...

  2. 分享一个旋钮插件 jquery.knob.js 新增change事件中回调返回值功能以及阻止冒泡事件

    本来都要休息了还是打开github,解决了线上一个插件的兼容性. 看来今晚能睡的比较踏实了! 插件介绍 jquery-knob是我用到的UI设计比较清新的旋钮插件之一,应用灵活,体积小,功能强大. 图 ...

  3. JQuery阻止冒泡事件on绑定中异常情况分析

    本文转载自https://www.cnblogs.com/tengj/p/4794947.html,纯粹作为日常笔记使用 科普下事件冒泡以及默认行为,以下面例子举列子: 事件冒泡:当点击内部butto ...

  4. vue中阻止冒泡事件

    vue冒泡事件 事件冒泡是指发生在子元素身上的事件,会冒泡至父元素身上.如我们在子元素身上点击后,也会触发父元素的点击事件,若不及时阻止,该事件还会一级一级冒上去.事件冒泡这个行为是默认存在的,故需要 ...

  5. html 传参阻止冒泡,angular阻止冒泡事件

    当元素多层嵌套的时候,每层都有点击事件,它就会发生冒泡,一层一层的触发,但有时候我们只想触发某一层,不想让其他层的事件触发,这就需要阻止冒泡事件了. 以angular项目为例,我们都知道angular ...

  6. 用vue的事件修饰符阻止冒泡

    用mousemove事件举例 1.传统做法: 定义一个阻止冒泡的函数stop,形参为事件e,执行e.stopPropagation(), 在标签上添加v-on:mousemove="stop ...

  7. uniCloud开发公众号:六、解析不同情况下用户扫码后微信推送的事件并完成登录

    算是个系列内容吧,最终要实现的是将uniCloud作为后端完成"扫码关注公众号后完成网站登录" 将要涉及的内容可能包括: 0.准备工作(本节) 1.接受并解析xml消息 2.请求a ...

  8. VB.NET下的事件和委托

    委托是可用于调用其他对象方法的对象.它们有时被称为类型安全函数指针,因为它们与其他编程语言中所使用的函数指针相似.但不同于函数指针,Visual Basic .NET 委托是基于 System.Del ...

  9. 在不熟悉C/C++情况下,hook windows事件

    本文讲的是在不熟悉C/C++情况下,hook windows事件, 介绍 MSDN中对它的介绍为: WMI(Windows 管理规范)是基于Windows操作系统的管理数据和操作的基础架构.可以编写W ...

最新文章

  1. aspose.word在某个字后面自动换行_在Arctime里制作字幕如何自动换行?如何添加注释、广告语?...
  2. Android 计算Bitmap大小
  3. C语言程序练习-L1-002 打印沙漏 (20分)
  4. 从Servlet 到ApplicationContext
  5. AWS实例上AMI和用户名的映射表
  6. 基于matlab 论文知网,基于MATLAB的校园图像处理与分析
  7. 如果身价千万,你还会事无巨细亲力亲为吗
  8. CUDA精进之路(三):图像处理——图像灰度化、灰度直方图统计
  9. 中画图title函数_Matlab对量子力学中的一维无限深势阱的模拟计算
  10. 你是如何看待 ‘裸辞’ 这件事的?
  11. 2021全国研究生数学建模竞赛E题思路
  12. NDK之FFmpeg视频解码
  13. 三阶魔方中心互换_三阶魔方入门
  14. 实现input框显示,但禁止输入
  15. linux关于日志文件介绍,Linux下重要日志文件介绍
  16. 一文揭秘阿里、腾讯、百度的薪资职级
  17. java计算机毕业设计校园环境保护监督系统源程序+mysql+系统+lw文档+远程调试
  18. python3从零学习-5.3.2、复数库cmath
  19. 求解二维矩形 Packing 问题的一种优美度枚举 算法的个人心得1
  20. 疫情之下的远程办公,基础架构成为重中之重

热门文章

  1. POI in Action
  2. linux 图像处理软件,linux下的图像处理软件
  3. Python3 百度IP 查询 接口
  4. 关于档案没有寄回生源地的解决方案
  5. st-link v2怎么连接_【粉猪测评】入门级游戏鼠标怎么选(200内)
  6. 阿里云rds数据库备份与恢复
  7. Android 环信即时通讯集成
  8. 温湿度变送器是干什么用的
  9. 【Unity】siki公开Unity相关课程下载目录
  10. 市场营销必须收藏使用的5个软件