目录
目录
- <style>
- *{margin: 0;}
- .box1{
- width: 400px;
- height: 300px;
- background-color: brown;
- cursor: pointer;
- position:relative;
- left: 100px;
- top: 20px;
- }
- .box2{
- width: 200px;
- height: 200px;
- background-color: red;
- cursor: pointer;
- position:absolute;
- left: 10px;
- top: 20px;
- }
- .box3{
- width: 100px;
- height: 100px;
- background-color: gold;
- cursor: pointer;
- margin: 10px;
- padding: 5px;
- border: 3px solid saddlebrown;
- }
- style>
- <div class="box1">
- <div class="box2">
- <div class="box3">div>
- div>
- div>
- <script>
- var box1 = document.querySelector('.box1');
- var box2 = document.querySelector('.box2');
- var box3 = document.querySelector('.box3');
- box1.onclick = function(){
- console.log('box111111');
- }
- script>
分析:只给box1绑定点击事件,点击box1的区域,包括box2和box3(都在box1的区域内),都会打印box111111
利用绝对定位把box2和box3放到box1区域红色区域外面去,再点击box2和box3会出现什么效果?
- <style>
- *{margin: 0;}
- .box1{
- width: 400px;
- height: 300px;
- background-color: brown;
- cursor: pointer;
- position:relative;
- left: 100px;
- top: 20px;
- }
- .box2{
- width: 200px;
- height: 200px;
- background-color: red;
- cursor: pointer;
- /* 让box2带着box3从box1的区域出去 */
- position:absolute;
- left: 500px;
- top: 20px;
- }
- .box3{
- width: 100px;
- height: 100px;
- background-color: gold;
- cursor: pointer;
- margin: 10px;
- padding: 5px;
- border: 3px solid saddlebrown;
- }
- style>
- <div class="box1">
- <div class="box2">
- <div class="box3">div>
- div>
- div>
- <script>
- var box1 = document.querySelector('.box1');
- var box2 = document.querySelector('.box2');
- var box3 = document.querySelector('.box3');
- box1.onclick = function(){
- console.log('box111111');
- }
- script>
分析:只给了box1绑定点击事件,然后把box2和box3移到box1外面的区域,为什么点击box2和box3还是会打印box111111。原因是box2是box1的子元素,box3又是box2的子元素,虽然通过绝对定位移出去了,但是还是box1的子元素。所以我们要研究事件是怎么执行的,跟父子关系有什么联系?
事件对象的path属性:
- <style>
- *{margin: 0;}
- .box1{
- width: 400px;
- height: 300px;
- background-color: brown;
- cursor: pointer;
- position:relative;
- left: 100px;
- top: 20px;
- }
- .box2{
- width: 200px;
- height: 200px;
- background-color: red;
- cursor: pointer;
- /* 让box2带着box3从box1的区域出去 */
- position:absolute;
- left: 500px;
- top: 20px;
- }
- .box3{
- width: 100px;
- height: 100px;
- background-color: gold;
- cursor: pointer;
- margin: 10px;
- padding: 5px;
- border: 3px solid saddlebrown;
- }
- style>
- <div class="box1">
- <div class="box2">
- <div class="box3">div>
- div>
- div>
- <script>
- var box1 = document.querySelector('.box1');
- var box2 = document.querySelector('.box2');
- var box3 = document.querySelector('.box3');
- box1.onclick = function(e){
- //e是事件对象
- console.log('box111111',e);
- }
- script>
分析:点击了box3,触发了box1的点击事件,生成的事件对象,然后查看其中的path属性 
就是点击事件所经过的层级(嵌套关系)。
但通常,一个事件会从父元素开始向目标元素传播(捕获),然后它将被传播回父元素(冒泡)。这个过程就是事件流/事件链.
DOM事件流(event flow )存在三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。
- 捕获阶段:事件从父元素开始向目标元素传播,从
Window对象开始传播。- 目标阶段:该事件到达目标元素或开始该事件的元素。
- 冒泡阶段:这时与捕获阶段相反,事件向父元素传播,直到
Window对象

无论是事件捕获还是事件冒泡,它们都有一个共同的行为,就是事件传播。
dom标准事件流的触发的先后顺序为:先捕获再冒泡。即当触发dom事件时,会先进行事件捕获,捕获到事件源之后通过事件传播进行事件冒泡。
所以上面最开始引入的例子:为什么点击box3,也会执行给box1绑定的点击事件?
因为:点击box3时,因为 box1的点击事件在捕获阶段并没有触发(addEventListener第3个参数没写或者写false代表冒泡阶段触发),然后一直沿着父子关系向下传递,直到最下面的box3,然后进入冒泡阶段,因为点击是点到box3,所以触发了box1传下来的点击事件,执行box1绑定的点击事件,打印box11111,所以 box3就是目标(target:谁触发得事件target就是谁 event.target = box3)

如果点击的是box2,事件经过的path,最底层就是box2,然后进入冒泡阶段,就不会在捕获阶段进入box3然后再从box3进入冒泡阶段:

如果是点击box1,事件经过的path,最底层就是box1,然后进入冒泡阶段,就不会在捕获阶段进入box3然后再从box3进入冒泡阶段:

如果点击box1的父元素body,直接就不会执行点击事件。
- <style>
- *{margin: 0;}
- .box1{
- width: 400px;
- height: 300px;
- background-color: brown;
- cursor: pointer;
- position:relative;
- left: 100px;
- top: 20px;
- }
- .box2{
- width: 200px;
- height: 200px;
- background-color: red;
- cursor: pointer;
- /* 让box2带着box3从box1的区域出去 */
- position:absolute;
- left: 500px;
- top: 20px;
- }
- .box3{
- width: 100px;
- height: 100px;
- background-color: gold;
- cursor: pointer;
- margin: 10px;
- padding: 5px;
- border: 3px solid saddlebrown;
- }
- style>
- <div class="box1">
- <div class="box2">
- <div class="box3">div>
- div>
- div>
- <script>
- var box1 = document.querySelector('.box1');
- var box2 = document.querySelector('.box2');
- var box3 = document.querySelector('.box3');
- box1.onclick = function(e){
- //e是事件对象
- console.log('box111111',e);
- }
- box2.onclick = function(e){
- //e是事件对象
- console.log('box222222',e);
- }
- box3.onclick = function(e){
- //e是事件对象
- console.log('box333333',e);
- }
- script>
分析:
点击box1,打印box111111 PointerEvent {}
点击box2:打印
box22222 PointerEvent {}
box111111 PointerEvent {}
点击box3:打印
box33333 PointerEvent {}
box22222 PointerEvent {}
box111111 PointerEvent {}
结合事件流,就可以很容易理解这个打印结果。
在我们平常用的addEventListener方法中,一般只会用到两个参数,一个是需要绑定的事件,另一个是触发事件后要执行的函数,然而,addEventListener还可以传入第三个参数:
第三个参数默认是false,也就是第三个参数不写或者写false: 表示在事件冒泡阶段调用事件处理函数;如果参数为true,则表示在事件捕获阶段调用处理函数。
- //给box1绑定两个事件,一个捕获阶段触发,一个冒泡阶段触发
- box1.addEventListener('click',(e)=>{
- console.log('box111111bbbbbbb',e);
- },true)
- box1.addEventListener('click',(e)=>{
- console.log('box111111bbbbbbb',e);
- },false)
问题: addEventListener的第三个参数为true是阻止事件传递还是false是阻止事件传递?
答案:都不会阻止事件传递,因为true捕获阶段触发,false冒泡阶段触发
要阻止事件传递,唯一的方式就是阻止事件冒泡,事件对象调用stopPropagation();
1.event.stopPropagation()方法
这是阻止事件的冒泡方法,不让事件向documen上蔓延,但是默认事件任然会执行,当你掉用这个方法的时候,如果点击一个连接,这个连接仍然会被打开,
2.event.preventDefault()方法
这是阻止默认事件的方法,调用此方法是,连接不会被打开,但是会发生冒泡,冒泡会传递到上一层的父元素;
- <style>
- .box1{
- width: 200px;
- height: 200px;
- background-color: blue;
- }
- .box2{
- width: 100px;
- height: 100px;
- background-color: pink;
- }
- style>
- <div class="box1">
- <div class="box2">
- <a href="https://www.baidu.com" id="a">跳到百度a>
- div>
- div>
- <script>
- var box1 = document.querySelector('.box1');
- var a = document.querySelector('#a');
- box1.addEventListener('click',(e)=>{
- console.log('box1111111');
- })
- a.addEventListener('click',(e)=>{
- console.log(666);
- //stopPropagation 阻止冒泡 但不阻止默认行为
- e.stopPropagation();
- //阻止默认行为,但不阻止冒泡 所以要想阻止默认行为,又要阻止冒泡,两个都写
- e.preventDefault();
- })
- script>
分析:a标签的默认行为,就是其默认有一个点击事件(官方设置的),点击它会跳转到相应的href地址去,如果通过 event.stopPropagation()方法 和event.preventDefault()方法设置了阻止冒泡和阻止默认行为后,之后打印 666
3.return false ;
这个方法比较暴力,他会同事阻止事件冒泡也会阻止默认事件
但是需要注意的是:
IE:
window.event.cancelBubble = true;//停止冒泡
window.event.returnValue = false;//阻止事件的默认行为
Firefox(与谷歌一样):
event.preventDefault();// 阻止事件的默认行为
event.stopPropagation(); // 阻止事件的传播
4.event.stopImmediatePropagation() 阻止事件冒泡 也阻止同一类型的其它事件程序从它这里过(同一类型的冒泡),阻止不了默认事件这些哈!!
- <style>
- *{margin: 0;}
- .box1{
- width: 400px;
- height: 300px;
- background-color: brown;
- cursor: pointer;
- position:relative;
- left: 100px;
- top: 20px;
- }
- .box2{
- width: 200px;
- height: 200px;
- background-color: red;
- cursor: pointer;
- /* 让box2带着box3从box1的区域出去 */
- position:absolute;
- left: 500px;
- top: 20px;
- }
- .box3{
- width: 100px;
- height: 100px;
- background-color: gold;
- cursor: pointer;
- margin: 10px;
- padding: 5px;
- border: 3px solid saddlebrown;
- }
- style>
- <div class="box1">
- <div class="box2">
- <div class="box3">div>
- div>
- div>
- <script>
- var box1 = document.querySelector('.box1');
- var box2 = document.querySelector('.box2');
- var box3 = document.querySelector('.box3');
- box1.addEventListener('click',(e)=>{
- console.log('box111111',e);
- })
- box3.addEventListener('click',(e)=>{
- console.log('box3333aaaaaa',e);
- e.stopPropagation();
- // e.stopImmediatePropagation();
- })
- box3.addEventListener('click',(e)=>{
- console.log('box3333bbbbb',e);
- })
- script>
分析:点击box3时,是阻止冒泡的情况下:e.stopPropagation();打印出
box3333aaaaaa PointerEvent{}
box3333bbbbbb PointerEvent{} 还好,虽然阻止冒泡,但至少本身其它的同一类型事件还能过。
点击box3时,阻止事件冒泡 也阻止同一类型的其它事件程序从它这里过 情况下 :e.stopImmediatePropagation();打印出
box3333aaaaaa PointerEvent{}
e.stopImmediatePropagation(); 就连本身绑定同一类型的第二个点击事件都经过不了,更不说冒泡了。
5.阻止事件冒泡和阻止事件默认情况的兼容性写法
- function(e){
- //得到事件对象
- var e=e||window.event;
- e.stopPropagation();//阻止冒泡
- // 兼容处理
- if (e.stopPropagation) {
- e.stopPropagation();
- }else{
- // IE浏览器
- e.cancelBubble=true;
- }
- }
- function(e){
- var e=e||window.event;
- e.defaultPrevent();//默认事件
- if (e.preventDefault) {
- //w3c
- e.preventDefault();
- } else{
- //ie
- e.returnValue = false;
- }
- }
1.阻止冒泡event.stopPropagation(),和阻止冒泡和程序经过stopImmediatePropagation() 都是针对的同一类型的事件。如果是两个不同类型,则并不能阻止另外一个。
- <style>
- *{margin: 0;}
- .box1{
- width: 400px;
- height: 300px;
- background-color: brown;
- cursor: pointer;
- position:relative;
- left: 100px;
- top: 20px;
- }
- .box2{
- width: 200px;
- height: 200px;
- background-color: red;
- cursor: pointer;
- /* 让box2带着box3从box1的区域出去 */
- position:absolute;
- left: 500px;
- top: 20px;
- }
- .box3{
- width: 100px;
- height: 100px;
- background-color: gold;
- cursor: pointer;
- margin: 10px;
- padding: 5px;
- border: 3px solid saddlebrown;
- }
- style>
- <div class="box1">
- <div class="box2">
- <div class="box3">div>
- div>
- div>
- <script>
- var box1 = document.querySelector('.box1');
- var box2 = document.querySelector('.box2');
- var box3 = document.querySelector('.box3');
- box1.addEventListener('click',(e)=>{
- console.log('box111111',e);
- })
- box2.addEventListener('mousedown',(e)=>{
- console.log('box22222',e);
- })
- box3.addEventListener('click',(e)=>{
- console.log('box3333aaaaaa',e);
- // e.stopPropagation();
- e.stopImmediatePropagation();
- })
- box3.addEventListener('click',(e)=>{
- console.log('box3333bbbbb',e);
- })
- script>
分析:box3中是针对于 click 类型的事件阻止冒泡和阻止 click事件经过。但并不阻止 mousedown 类型事件通过。所以,点击box3区域,不管是e.stopPropagation();还是e.stopImmediatePropagation();这种情况下,都能打印出 box22222 PointerEvent{ }
UI事件:
load
unload
resize
abort
error
焦点事件:
blur
focus
鼠标事件:
mouseleave
mouseenter
有一个比较特殊:
scroll 事件:element的scroll事件不冒泡, 但是document的defaultView的scroll事件冒泡