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 写类方式之二
Jul 05 Javascript
详解强大的jQuery选择器之基本选择器、层次选择器
Feb 07 Javascript
使用jquery实现div的tab切换实例代码
May 27 Javascript
jQuery代码实现发展历程时间轴特效
Jul 30 Javascript
jQuery实现form表单基于ajax无刷新提交方法详解
Dec 08 Javascript
js实现新年倒计时效果
Dec 10 Javascript
jquery调整表格行tr上下顺序实例讲解
Jan 09 Javascript
使用jQuery中的wrap()函数操作HTML元素的教程
May 24 Javascript
jQuery中使用animate自定义动画的方法
May 29 Javascript
vue轮播图插件vue-awesome-swiper的使用代码实例
Jul 10 Javascript
基于Vue实例对象的数据选项
Aug 09 Javascript
微信小程序vant弹窗组件的实现方式
Feb 21 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
php中文件上传的安全问题
2006/10/09 PHP
Zend Studio去除编辑器的语法警告设置方法
2012/10/24 PHP
PHP小教程之实现链表
2014/06/09 PHP
详解WordPress中分类函数wp_list_categories的使用
2016/01/04 PHP
php实现在站点里面添加邮件发送的功能
2020/04/28 PHP
Yii2框架数据库简单的增删改查语法小结
2016/08/31 PHP
PHP实现基于栈的后缀表达式求值功能
2017/11/10 PHP
PHP实现负载均衡的加权轮询方法分析
2018/08/22 PHP
css动画效果之animation的常用样式
2021/03/09 HTML / CSS
浏览器加载、渲染和解析过程黑箱简析
2012/11/29 Javascript
jQuery建立一个按字母顺序排列的友好页面索引(兼容IE6/7/8)
2013/02/26 Javascript
基于Jquery实现焦点图淡出淡入效果
2015/11/30 Javascript
JS获取年月日时分秒的方法分析
2016/11/28 Javascript
从零开始学习Node.js系列教程三:图片上传和显示方法示例
2017/04/13 Javascript
详解React中传入组件的props改变时更新组件的几种实现方法
2018/09/13 Javascript
JS实现数组深拷贝的方法分析
2019/03/06 Javascript
如何利用node转发请求详解
2020/09/17 Javascript
使用Python操作excel文件的实例代码
2017/10/15 Python
python多线程之事件Event的使用详解
2018/04/27 Python
python3的输入方式及多组输入方法
2018/10/17 Python
Python 抓取微信公众号账号信息的方法
2019/06/14 Python
在pycharm下设置自己的个性模版方法
2019/07/15 Python
SpringBoot实现登录注册常见问题解决方案
2020/03/04 Python
浅谈keras使用预训练模型vgg16分类,损失和准确度不变
2020/07/02 Python
python中plt.imshow与cv2.imshow显示颜色问题
2020/07/16 Python
世界上最大的网络主机公司:1&1
2016/10/12 全球购物
Jimmy Choo美国官网:周仰杰鞋子品牌
2018/06/08 全球购物
美国环保妈妈、儿童和婴儿用品购物网站:The Tot
2019/11/24 全球购物
.net面试题
2016/09/17 面试题
公务员总结性个人自我评价
2013/12/05 职场文书
信息技术培训感言
2014/03/06 职场文书
幼儿园三八妇女节活动方案
2014/03/11 职场文书
考核评语大全
2014/04/29 职场文书
物业保安岗位职责
2014/07/02 职场文书
关于感恩的演讲稿400字
2014/08/26 职场文书
故意杀人案辩护词
2015/05/21 职场文书