JavaScript 事件代理需要注意的地方


Posted in Javascript onSeptember 08, 2020

我们知道,如果给 form 里面的 button 元素绑定事件,需要考虑它是否会触发 form 的 submit 行为。除此之外,其它场合给 button 元素绑定事件,你几乎不用担心这个事件会有什么非预期的附加效果,很自然地会这样写事件处理代码:

var button = document.querySelector('button')
button.addEventListener('click', function (e) {
 console.log('点击了按钮')
})

你之所以放心这么写,是因为这个 button 元素没有使用事件代理,即没有代理任何子元素的事件。

事件代理的意思是,你要为一个元素绑定事件,但你不是直接把事件绑定到这个元素自己身上,而是绑定到这个元素的父元素上。当子元素的某个事件(比如点击事件)触发时,它的父元素相同的事件也会触发(我们常说的事件冒泡),此时我们说父元素代理了子元素的事件。

举个例子,比如一个 button 元素中包含一个齿轮图标:

<button>
 <svg>
 <use xlink:href="#gear" rel="external nofollow" ></use>
 </svg>
</button>

当用户点击齿轮图标,必然要触发 click 事件,但你并不会直接绑定事件到 svg 或 use 元素上,而是绑定到它们的父元素 button 上。即:

document.querySelector('button').addEventListener('click', function (e) {
 console.log('点击了按钮')
})

这种情况,我们可以说,button 元素代理了它的所有子元素的 click 事件。

但是,出现这种事件代理的情况时,我们就得小心了。

为了更直观地说明问题,我们把“父”元素上升到顶层的 document 元素:

document.documentElement.addEventListener('click', function (e) {
 console.log('我被点击了')
})

只要网页中任意一个位置被点击了,都会触发绑定在 document 元素上的点击事件。​ 想要知道事件具体是发生在哪个元素上面,可以通过事件对象提供的 target 属性来判断。

document.documentElement.addEventListener('click', function (e) {
 console.log(e.target)
})

我们很容易知道事件具体是发生在哪个元素身上的。于是在上面的示例中,如果父元素 document 想在按钮被点击时做点什么事情,我们很自然地会这么写:

document.documentElement.addEventListener('click', function (e) {
 if (e.target.tagName === 'BUTTON') {
 console.log('按钮被点击了')
 }
})

这时问题就出现了,按钮即使被点击了 if 条件也不一定成立,即也不一定会输出“按钮被点击了”。因为用户在按钮上的某个位置点击了,根据用户点击的位置,e.target 可能是下面三种情况:

  • BUTTON 元素
  • SVG 元素
  • USE 元素

实际的情况是这样的:

JavaScript 事件代理需要注意的地方

我们真正的意图是,只要点击是发生在按钮上面,不论是按钮的哪个位置,我们都应视为按钮被点击了。 嗯,简单,我们再改一下,这样写:

document.documentElement.addEventListener('click', function (e) {
 if (['BUTTON', 'SVG', 'USE'].includes(e.target.tagName.toUpperCase())) {
 // 点击的是按钮
 }
})

这样似乎没什么问题,也确实可以达到目的,但看上去总是有些别扭。因为这种情况对于最上层的 document 来说,得知道每个子元素的情况,本来我只需要关心离我最近的 button 元素就可以了。

根据 OOP 对内封装的思想,button 元素内部的事情应该在内部消化掉,其子元素对外不可见,应该只暴露 button 元素本身。依据这个思想和事件冒泡的特点,我们就有了比较好的解决办法:只需要禁止 button 内部元素的事件响应(包括事件冒泡)而只允许 button 元素本身的事件发生就行。有两种方式可以实现这个目的。

一种是使用 CSS 禁止 button 内部元素的事件响应:

button > * {
 pointer-events: none;
}

另一种是使用 JS 来阻止 button 内部元素的事件响应(包括事件冒泡):

document.querySelector('button > svg').addEventListener('click', function (e) {
 e.stopPropagation()
 e.preventDefault()
})

document.querySelector('button').addEventListener('click', function (e) {
 console.log(e.target.tagName)
})

这两种方式都能达到我们预期的效果:

JavaScript 事件代理需要注意的地方

综上,针对特定元素进行事件处理时,如果该元素有事件代理的情况,就要小心处理它所代理的子元素。

以上就是JavaScript 事件代理需要注意的地方的详细内容,更多关于JavaScript 事件代理的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
将HTML自动转为JS代码
Jun 26 Javascript
JQuery的html(data)方法与&amp;lt;script&amp;gt;脚本块的解决方法
Mar 09 Javascript
CSS和JS标签style属性对照表(方便js开发的朋友)
Nov 11 Javascript
使用jquery mobile做幻灯播放效果实现步骤
Jan 04 Javascript
jquery通过closest选择器修改上级元素的方法
Mar 17 Javascript
node.js平台下的mysql数据库配置及连接
Mar 31 Javascript
AngularJS使用ng-repeat遍历二维数组元素的方法详解
Nov 11 Javascript
Vue实现侧边菜单栏手风琴效果实例代码
May 31 Javascript
微信小程序实现弹出层效果
May 26 Javascript
webpack 如何解析代码模块路径的实现
Sep 04 Javascript
vue组件开发之tab切换组件使用详解
Aug 21 Javascript
JS轻量级函数式编程实现XDM三
Jun 16 Javascript
Vue axios 跨域请求无法带上cookie的解决
Sep 08 #Javascript
详解JavaScript的this指向和绑定
Sep 08 #Javascript
vue点击按钮实现简单页面的切换
Sep 08 #Javascript
Vue filter 过滤器、以及在table中的使用介绍
Sep 07 #Javascript
VUE中setTimeout和setInterval自动销毁案例
Sep 07 #Javascript
vue a标签点击实现赋值方式
Sep 07 #Javascript
JavaScript实现多球运动效果
Sep 07 #Javascript
You might like
计算2000年01月01日起到指定日的天数
2006/10/09 PHP
PHP图片处理之使用imagecopyresampled函数实现图片缩放例子
2014/11/19 PHP
laravel 框架配置404等异常页面
2019/01/07 PHP
对php 判断http还是https,以及获得当前url的方法详解
2019/01/15 PHP
Yii使用DbTarget实现日志功能的示例代码
2020/07/21 PHP
斜45度寻路实现函数
2009/08/20 Javascript
javascript 面向对象编程 聊聊对象的事
2009/09/17 Javascript
jQuery1.6 类型判断实现代码
2011/09/01 Javascript
js将long日期格式转换为标准日期格式实现思路
2013/04/07 Javascript
Jquery 监视按键,按下回车键触发某方法的实现代码
2014/05/11 Javascript
详解javascript遍历方式
2015/11/11 Javascript
JS实现拖拽的方法分析
2016/12/20 Javascript
老生常谈jquery id选择器和class选择器的区别
2017/02/12 Javascript
在vue中添加Echarts图表的基本使用教程
2017/11/22 Javascript
angular第三方包开发整理(小结)
2018/04/19 Javascript
JS实现select选中option触发事件操作示例
2018/07/13 Javascript
原生js canvas实现鼠标跟随效果
2020/08/02 Javascript
[54:08]LGD女子刀塔学院 DOTA2炼金术士教学
2014/01/09 DOTA
解读! Python在人工智能中的作用
2017/11/14 Python
Python中的默认参数实例分析
2018/01/29 Python
python读取各种文件数据方法解析
2018/12/29 Python
关于Python 解决Python3.9 pandas.read_excel(‘xxx.xlsx‘)报错的问题
2020/11/28 Python
详解Python中的Lock和Rlock
2021/01/26 Python
CSS3解析抖音LOGO制作的方法步骤
2019/04/11 HTML / CSS
HTML5实现晶莹剔透的雨滴特效
2014/05/14 HTML / CSS
Tiqets荷兰:出售欧洲最美丽的景点和博物馆门票
2018/01/09 全球购物
中国包裹转运寄送国际服务:Famiboat
2019/07/24 全球购物
俄语地区最大的中国商品在线购物网站之一:Umka Mall
2019/11/03 全球购物
经典c++面试题四
2015/05/14 面试题
儿科护士实习自我鉴定
2013/10/17 职场文书
副总经理工作职责
2013/11/28 职场文书
2014年国庆节演讲稿精选范文1500字
2014/09/25 职场文书
摩登时代观后感
2015/06/03 职场文书
2015中学学校工作总结
2015/07/20 职场文书
2021年pycharm的最新安装教程及基本使用图文详解
2021/04/03 Python
Spring Bean的实例化之属性注入源码剖析过程
2021/06/13 Java/Android