## js事件冒泡
事件是监听在某个DOM元素上的,但是js的DOM事件有捕获和冒泡的机制,所以事件处理不是我们想的那样简单。
由于存在捕获和冒泡,所以事件的触发元素(目标源)不一定是当前的监听元素。于是就有一些问题,本文要解决的就是将这些问题整理叙述清楚。
**关键点:**
- event 事件对象
- 目标(源) target
- 当前目标(源) currentTarget
- 元素 element(当前捕获的元素和事件源目标元素)
**要解决两个问题:**
1. 阻止事件从当前元素上往上层冒泡
2. 识别出当前事件的目标源,判断是不是冒泡上来的事件
~~~javascript
// 二级菜单单击事件
$(document).on('click', '.Level_2_menu__btn', function(e) {
var e = e || window.event;
var target = e.tagName || e.srcElement || e.toElement || e.target;
var className = target.className || $(target).attr('className');
if (className.indexOf('Level_2__button') == -1) {
return false;
}
// alert(target.className);
});
~~~
* * * * *
### 参考
[JS事件:target与currentTarget区别 - wkylin - 博客园](https://www.cnblogs.com/wkylin/archive/2011/08/25/2153538.html)
> 上面的示例中,当在outer上点击时,e.target与e.currentTarget是一样的,都是div;当在inner上点击时,e.target是p,而e.currentTarget则是div。
[事件 · 小程序](https://mp.weixin.qq.com/debug/wxadoc/dev/framework/view/wxml/event.html)(参见:target/currentTarget)
> target:触发事件的组件的一些属性值集合;currentTarget:当前组件的一些属性值集合。
[jQuery之防止冒泡事件 - TBHacker - 博客园](http://www.cnblogs.com/jiqing9006/archive/2012/09/11/2679831.html)
>return false 会把浏览器默认事件和冒泡干掉,ev.preventDefualt(); 可以单独阻止默认事件,ev.stopPropagation() 可以单独阻止冒泡
[JQuery中阻止事件冒泡方式及其区别 - JeamKing的专栏 - CSDN博客](http://blog.csdn.net/JeamKing/article/details/5332328/)
[JS事件委托的原理和应用 - 追梦子 - 博客园](http://www.cnblogs.com/pssp/p/5253284.html)
[JS Event事件 - 追梦子 - 博客园](http://www.cnblogs.com/pssp/p/5277749.html)
[走进javascript——DOM事件 - 追梦子 - 博客园](http://www.cnblogs.com/pssp/p/6382874.html)
[addEventListener和on的区别 - 追梦子 - 博客园](http://www.cnblogs.com/pssp/p/5196716.html)
[JQuery阻止冒泡事件on绑定中异常情况分析 - 胖逆的嘟嘟 - 博客园](http://www.cnblogs.com/tengj/p/4794947.html)
[解析Javascript事件冒泡机制 - 我的程序人生 - CSDN博客](http://blog.csdn.net/luanlouis/article/details/23927347)
[javascript冒泡事件的意义及如何阻止冒泡事件 - u012169411的专栏 - CSDN博客](http://blog.csdn.net/u012169411/article/details/16804233)
[游览器中有哪些事件会冒泡?](https://segmentfault.com/q/1010000000687977)
[JS事件流与DOM事件处理程序](https://www.toutiao.com/i6392562710684369410/)
[事件捕获和事件冒泡什么意思?-coder分享的回答-悟空问答](https://www.wukong.com/answer/6492960953607389453/?iid=12619555732&app=news_article&share_ansid=6492960953607389453&wxshare_count=1&tt_from=weixin&utm_source=weixin&utm_medium=toutiao_android&utm_campaign=client_share)
* * * * *
### 总结
**这些文章都是讲如何阻止事件往上冒泡,也就是解决的是第一个问题,并没有说到如何解决第二个问题,“怎么防止事件是冒泡上来的?”**,其实要解决第二个问题需要自己判断目标源,或者使用事件代理,$.on(),这其实是将事件监听(代理)在父元素上面,内部也是利用了冒泡的机制,并判断目标源,实现事件代理的。
为了不必要的麻烦(事件触发有可能是由被真正触发的子元素冒泡上来的),一般请尽量使用事件代理on来绑定事件(**三个参数**,两个参数的话就和 $("#id").click() 差不多,避免不了事件可能是由冒泡上来的了,还得自己判断)。
* * * * *
### 事件触发时机的探索
事件有三种触发时机:
1. 直接触发,事件源目标上面的事件。
2. 接收到冒泡时是否能被触发。
3. 捕获阶段是否能被触发。2和3只能同时存在一种,1都存在。所以实际的情况都是两种触发时机,1,2 和 1,3。不过捕获用得少,并且默认事件定义都是冒泡,所以大多情况,元素的事件都是 1,2 (直接触发和冒泡) 两种触发时机。
```javascript
$('#id').click(function(event){
// 注意这里的event和window.event是不同的,前者是Jq传进来的实参,后者是全局变量,事件发生时会产生的,每个事件现场都有一个这个全局变量。
var event = event ? event : window.event是不同的,前者是Jq传进来的,后者是全局的,事件发生时会产生的。;
// 阻止向上冒泡(即使当前不为事件目标源时也会阻止冒泡,即别人的泡也是可以阻止的,不过要知道泡是直接往上的,是从目标源往上冒的)
event.stopPropagation();
// 阻止默认事件,比如表单提交、防止链接打开 URL
event.preventDefault();
// 事件目标源
var target = event.srcElement ? event.srcElement : event.target;
// 当前事件触发的元素
var that = this;
// target不一定等于this,this可能是冒泡上来的事件,target是直接触发的目标源
// 事件源目标元素的标签名 a
console.log( target.localName);
// 事件源目标元素的类名
console.log(target.className);
});
// 注意:这是等同的
// return false; 《=》 event.stopPropagation();event.preventDefault();
```
**(重点:别人的泡也是可以阻止的?)**
待仔细验证证明
* * * * *
默认事件是最后才被触发的事件,所以这样能阻止回车提交:
```javascript
// 禁止回车提交
$(document).on('keydown', 'form', function() {
if (event.keyCode == 13) {
return false;
}
});
```
* * * * *
### 冒泡实战
分析Bootstrap的下拉组件dropdown还有看云的下拉组件:
![](https://box.kancloud.cn/7439a1292ac28ec690a375e385a52f10_409x471.png)
点击`header部分`和分割线不会关闭下拉,点击页面其它位置都会关闭下拉。
并且看云和`Bootstrap`的下拉都有这样的功能,那就是点开一个下拉展开,然后点击另一个下拉,那么前一个下拉关闭,这个点击展开新的下拉,也就是在多个下拉组件中点击,总会有一个出于展开,一个处于关闭,如果是监听在`document`的点击事件上,那么应该都关闭了啊,所以展开触发按钮应该阻止了事件冒泡,但是那又怎么关闭其它的呢,可见这其中有一定的设计,有时间好好研究下。
>[danger] 应该是目标元素上绑定展开事件(注意,目标元素触发时是不分冒泡还是捕获的),然后`document`上绑定的是捕获的关闭事件(关闭所有的层),这样`document`捕获事件先发生(屏幕上的任何点击事件,`document`的捕获事件都是第一个被触发的),就不能关闭还未触发的展开事件创建的层了,这样就能做到点击时一个处于关闭一个处于展开的效果了。
这个比较能体现浏览器中js的事件机制(捕获,冒泡)。
>[nice] 其实很简单,没那么复杂,d和下拉都是正常事件,下拉事件只需要控制当前的目标下拉与关闭就可以了,d是一个事件代理,关闭所有下拉,并取消活跃,但有一个情况例外,如果源为一个下拉的话,关闭就排除这个下拉。这样逻辑就正常了。(有赞商城底部菜单和看云就是这样的,这样的体验才是正确的。)
~~~
shopnc 的管理后台商品分类那里,更改分类名,用到了input的失去焦点事件的特性,不得不说这个天然的事件特性很巧妙。
shopnc nctouch-nav-menu 菜单,弹出时,使用一个透明的遮罩层,遮住整个页面,但它又在菜单的下面一层,这样就巧妙实现了,点其它地方(其实就是点到那个透明的遮罩层了)关闭菜单。
~~~
* * * * *
这个地方有一个衔接,从这个衔接位置移出,2就不会隐藏了,不然从1移出2就立即消失,就导致鼠标不能移动到2上面了,衔接解决了这个问题。
![](http://cdn.aipin100.cn/18-3-21/16508543.jpg)
* * * * *
![](http://cdn.aipin100.cn/18-5-9/57628780.jpg)
* * * * *
做一个 tips 插件, 鼠标可以移动上去的(从尖尖那里可以移上去)
* * * * *
### TODO: return false;能阻止子元素的事件?
```javascript
// html 结构:li.news-category-item>a
// 选中的再次点击就会取消
$('.news-category-item').click(function(event) {
if ($(this).hasClass('active')) {
// window.location.href = "{:url('Article/news')}";
return false;
} else {
return true;
}
});
```
再次用`event.preventDefault()`和`event.stopPropagation();`实验了一下,原来不是阻止冒泡了,我说怎么会这么奇怪,怎么可能还能阻止子元素的事件呢,而是阻止默认事件了。
`return false === 阻止默认事件 + 阻止冒泡`
不过这个能阻止子元素的默认事件,这是由子元素而冒泡触发的事件啊,它不是事件源啊,也真是够奇怪的,有时间再来研究。
* * * * *
last update:2018-6-26 17:48:43
- 开始
- 微信小程序
- 获取用户信息
- 记录
- HTML
- HTML5
- 文档根节点
- 你真的了解script标签吗?
- 文档结构
- 已经落后的技术
- form表单
- html实体
- CSS
- css优先级 & 设计模式
- 如何编写高效的 CSS 选择符
- 笔记
- 小计
- flex布局
- 细节体验
- Flex
- Grid
- tailwindcss
- JavaScript
- javascript物语
- js函数定义
- js中的数组对象
- js的json解析
- js中数组的操作
- js事件冒泡
- js中的判断
- js语句声明会提前
- cookie操作
- 关于javascript你要知道的
- 关于innerHTML的试验
- js引擎与GUI引擎是互斥的
- 如何安全的修改对象
- 当渲染引擎遇上强迫症
- 不要使用连相等
- 修改数组-对象
- 算法-函数
- 事件探析
- 事件循环
- js事件循环中的上下文和作用域的经典问题
- Promise
- 最佳实践
- 页面遮罩加载效果
- 网站静态文件之思考
- 图片加载问题
- 路由及转场解决方案
- web app
- 写一个页面路由转场的管理工具
- 谈编程
- 技术/思想的斗争
- 前端技术选型分析
- 我想放点html模板代码
- 开发自适应网页
- 后台前端项目的开发
- 网站PC版和移动版的模板方案
- 前后端分离
- 淘宝前后端分离
- 前后端分离的思考与实践(一)
- 前后端分离的思考与实践(二)
- 前后端分离的思考与实践(三)
- 前后端分离的思考与实践(四)
- 前后端分离的思考与实践(五)
- 前后端分离的思考与实践(六)
- 动画
- 开发小技巧
- Axios
- 屏幕适配
- 理论基础
- 思考
- flexible.js原理
- 实验
- rem的坑,为什么要设置成百分比,为什么又是62.5%
- 为什么以一个标准适配的,其它宽度也能同等适配
- 自适应、响应式、弹性布局、屏幕适配
- 适配:都用百分比?
- 番外篇
- 给你看看0.5px长什么样?
- 用事实证明viewport scale缩放不会改变rem元素的大小
- 为什么PC端页面缩放不会影响rem元素
- 究竟以哪个为设备独立像素
- PC到移动端初试
- 深入理解px
- 响应式之栅格系统
- 深入理解px(二)
- 一篇搞定移动端适配
- flex版栅格布局
- 其他
- 浏览器加载初探
- 警惕你的开发工具
- JS模块化
- webpack
- 打包原理
- 异步加载
- gulp
- 命名规范
- 接口开发
- sea.js学习
- require.js学习
- react学习
- react笔记
- vue学习
- vue3
- 工具、技巧
- 临时笔记
- 怎么维护好开源项目
- 待办
- 对前端MVV*C框架的思考
- jquery问题
- 临时
- 好文
- 节流防抖