JS 两种事件处理模型:事件冒泡和事件捕获

参考链接
讲事件处理模型之前先来了解浏览器的事件分发机制

事件分发

浏览器中的事件分发机制,指的是事件是如何在DOM树中传递的

浏览器的发展历史
初,网络世界一片混沌,一个名叫Netscape的浏览器横空出世,成为了网络世界早期真正意义上的领航者。Netscape为浏览器的发展做出了很多贡献,其很多功能后来成为了浏览器的标准。其中就包括本文所要探讨的事件模型。

对于事件传递机制,Netscape给出了自己的答案,从上往下传,即一个事件发生之后,会从文档的根一直传递到目标元素,也就是window -> document -> html -> body -> … -> target 的传递方向。这种传递模型,也就是现在所谓的事件捕获
有利益的地方就会有竞争。为了争夺浏览器份额,微软也开始着手浏览器的开发,推出了Netscape的最大竞争对手,IE浏览器。或许是为了显得与Netscape不一样,又或许是微软有自己的考虑,在事件传递机制上,微软的答案与Netscape截然相反,从下往上传,即一个事件发生之后,会从目标元素向上传递,一直传递的根元素,也就是 target -> parent -> … -> body -> html -> document -> window 的传递方向。这种传递模型,也就是现在的事件冒泡
对于攻城略地阶段的浏览器厂家来说,标准不统一还可以接受,但却苦了一众开发者。后来,Netscape日渐式微,IE独领风骚,w3c开始收拾这种混乱的局面,为事件传递机制定了一个统一的标准,浏览器既要实现事件捕获,也要实现事件冒泡。其实w3c制定这种标准也是不得已而为之,是一种兼容原Netscape页面的举动,毕竟,尽管Netscape倒了,Netscape里运行的页面还在,开发者还在。这一标准也就慢慢发展成了现在浏览器的通用标准。

在这里插入图片描述
既然w3c制定了标准,那么正常情况下,事件的传递方向就已经是固定的了,即window -> document -> html -> body -> … -> target -> … -> body -> html -> document -> window的一个循环。按照这种逻辑,我们每设置一个监听器,在一次事件传递过程中,都将被触发两次,但事实真的如此吗?我们看下面的代码。

定义页面上三个叠放的div,给每个div绑定点击事件,输出各自的名字

<div class="wrapper">
  <div class="content">
    <div class="box"></div>
  </div>
</div>
<script>
var wrapper = document.getElementsByClassName('wrapper')[0];
var content = document.getElementsByClassName('content')[0];
var box = document.getElementsByClassName('box')[0];

wrapper.addEventListener('click',function(){
  console.log('wrapper')
},false);
content.addEventListener('click',function(){
  console.log('content')
},false);
box.addEventListener('click',function(){
  console.log('box')
},false);

</script>

在这里插入图片描述

点击橘色方块,显示wrapper;点击黄色方块,显示content、wrapper;点击粉色方块,显示box、content、wrapper。
像是从点击的方块开始触发事件一步步往下层方块渗透,这种现象叫做事件冒泡

事件冒泡

结构上(非视觉上) 嵌套/父子关系的元素,会存在事件冒泡的功能,即同一事件,会从子元素冒泡向父元素。(自底向上)

现在我给黄色方块和粉色方块加上左外边距,使之变成如下的结构,再次点击各个方块

可以看到即便三个div不再叠加,事件仍会从子元素冒泡向父元素

故事件冒泡是存在于代码结构上的嵌套,并非视觉上的嵌套
在这里插入图片描述
但是,上面提到的事件分发机制不是说浏览器既要实现事件冒泡又要实现事件捕获吗,这里的捕获阶段的事件好像没有触发哎

确实,因为我们没有打开事件捕获的开关

事件捕获 (IE不能捕获事件)

结构上(非视觉上) 嵌套关系的元素,会存在事件捕获功能,即同一事件,会从父元素捕获至子元素(事件源元素)。(自顶向下)

开启方式:ele.addEventListener方法的第三个参数设为true – ele.addEventListener(type,fn,useCapture)

当useCapture=true时,意味着事件监听器将在捕获阶段被触发,而useCapture=false时,事件监听器将在冒泡阶段触发。useCapture默认为false。

<div class="wrapper">
  <div class="content">
    <div class="box"></div>
  </div>
</div>
<script>
var wrapper = document.getElementsByClassName('wrapper')[0];
var content = document.getElementsByClassName('content')[0];
var box = document.getElementsByClassName('box')[0];

wrapper.addEventListener('click',function(){
  console.log('wrapper')
},true);
content.addEventListener('click',function(){
  console.log('content')
},true);
box.addEventListener('click',function(){
  console.log('box')
},true);

点击粉色方块,首先最外层的橘色方块捕获事件并执行输出wrapper,接着中间层黄色方块捕获事件并执行输出content,最后被直接点击的粉色方块不需要捕获直接执行输出box
在这里插入图片描述

一个对象的一种事件类型上绑定的一个函数只能遵循一种事件处理模型
如果给一个对象的一种事件类型绑定两个函数,让这两个函数分别遵循事件冒泡和事件捕获的处理模型,那么触发这个事件后,会先执行遵循事件冒泡的函数还是遵循事件捕获的函数呢?

var wrapper = document.getElementsByClassName('wrapper')[0];
var content = document.getElementsByClassName('content')[0];
var box = document.getElementsByClassName('box')[0];

wrapper.addEventListener('click',function(){
  console.log('wrapperBubble')
},false);
content.addEventListener('click',function(){
  console.log('contentBubble')
},false);
box.addEventListener('click',function(){
  console.log('boxBubble')
},false);

wrapper.addEventListener('click',function(){
  console.log('wrapper')
},true);
content.addEventListener('click',function(){
  console.log('content')
},true);
box.addEventListener('click',function(){
  console.log('box')
},true);

点击粉色方块⬇
先触发事件捕获:
最外层的橘色方块捕获并执行,输出wrapper,接着中间层的黄色方块捕获并执行,输出content,最后被直接点击的粉色方块不需要捕获直接执行;

后触发事件冒泡:被直接点击的粉色方块执行,接着事件冒泡到中间层黄色方块,输出contentBubble,最后冒泡到最外层橘色方块,输出wrapperBubble

粉色方块有两个紧邻的事件执行,而事件执行的顺序遵循 谁先绑定谁先执行 的原则,故先输出boxBubble,后box

触发顺序:先捕获,后冒泡
在这里插入图片描述
并不是所有事件都有冒泡功能,如 focus、blur、change、submit、reset、select 等事件不冒泡

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值