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 相关文章推荐
javascript while语句和do while语句的区别分析
Dec 08 Javascript
分别用marquee和div+js实现首尾相连循环滚动效果,仅3行代码
Sep 21 Javascript
在JS数组特定索引处指定位置插入元素
Jul 27 Javascript
JS文字球状放大效果代码分享
Aug 19 Javascript
jQuery实现的登录浮动框效果代码
Sep 26 Javascript
基于jquery实现ajax无刷新评论
Aug 19 Javascript
EasyUI Combobox设置默认值 获取text的方法
Nov 28 Javascript
jQuery日期范围选择器附源码下载
May 23 jQuery
vue将时间戳转换成自定义时间格式的方法
Mar 02 Javascript
vue axios整合使用全攻略
May 24 Javascript
使用JS实现导航切换时高亮显示的示例讲解
Aug 22 Javascript
vue实现学生信息管理系统
May 30 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
超级简单的发送邮件程序
2006/10/09 PHP
基于php缓存的详解
2013/05/15 PHP
php用户密码加密算法分析【Discuz加密算法】
2016/10/12 PHP
Yii框架弹出框功能示例
2017/01/07 PHP
Thinkphp5.0框架视图view的循环标签用法示例
2019/10/12 PHP
google 搜索框添加关键字实现代码
2010/04/24 Javascript
javascript对数组的常用操作代码 数组方法总汇
2011/01/27 Javascript
JSDoc 介绍使用规范JsDoc的使用介绍
2011/02/12 Javascript
20款超赞的jQuery插件 Web开发人员必备
2011/02/26 Javascript
JavaScript中的apply()方法和call()方法使用介绍
2012/07/25 Javascript
页面只能打开一次Cooike如何实现
2012/12/04 Javascript
jquery实现人性化的有选择性禁用鼠标右键
2014/06/30 Javascript
深入理解JavaScript系列(45):代码复用模式(避免篇)详解
2015/03/04 Javascript
js行号显示的文本框实现效果(兼容多种浏览器 )
2015/10/23 Javascript
Javascript中浏览器窗口的基本操作总结
2016/08/18 Javascript
jQuery插件FusionCharts绘制的2D条状图效果【附demo源码】
2017/05/13 jQuery
vue中SPA单页面应用程序详解
2017/11/07 Javascript
vue 中Virtual Dom被创建的方法
2019/04/15 Javascript
Node.js爬虫如何获取天气和每日问候详解
2019/08/26 Javascript
微信小程序实现转盘抽奖
2020/09/21 Javascript
Python编程语言的35个与众不同之处(语言特征和使用技巧)
2014/07/07 Python
Django1.7+python 2.78+pycharm配置mysql数据库教程
2014/11/18 Python
python操作oracle的完整教程分享
2018/01/30 Python
Python统计时间内的并发数代码实例
2019/12/28 Python
Python Selenium参数配置方法解析
2020/01/19 Python
python 实现性别识别
2020/11/21 Python
阿联酋团购网站:Groupon阿联酋
2016/10/14 全球购物
YOOX台湾:意大利奢侈品电商
2018/10/13 全球购物
小米官方旗舰店:Xiaomi
2020/08/07 全球购物
料理师求职信
2014/01/30 职场文书
中班教师个人总结
2015/02/05 职场文书
初中生思想道德自我评价
2015/03/09 职场文书
Python还能这么玩之用Python做个小游戏的外挂
2021/06/04 Python
Go Plugins插件的实现方式
2021/08/07 Golang
Python Pygame实战在打砖块游戏的实现
2022/03/17 Python
Win11 Dev 预览版25174.1000发布 (附更新修复内容汇总)
2022/08/05 数码科技