• DOM 事件流(事件捕获和事件冒泡)


    目录

    目录

    1.案例引入事件流:

    2.事件流

    3.证明事件执行顺序是按事件流来的

    3.addEventListener的第三个参数

    4.阻止事件冒泡和默认事件

    5.易错点

    不支持冒泡的事件:


    1.案例引入事件流:

    1. <style>
    2. *{margin: 0;}
    3. .box1{
    4. width: 400px;
    5. height: 300px;
    6. background-color: brown;
    7. cursor: pointer;
    8. position:relative;
    9. left: 100px;
    10. top: 20px;
    11. }
    12. .box2{
    13. width: 200px;
    14. height: 200px;
    15. background-color: red;
    16. cursor: pointer;
    17. position:absolute;
    18. left: 10px;
    19. top: 20px;
    20. }
    21. .box3{
    22. width: 100px;
    23. height: 100px;
    24. background-color: gold;
    25. cursor: pointer;
    26. margin: 10px;
    27. padding: 5px;
    28. border: 3px solid saddlebrown;
    29. }
    30. style>
    31. <div class="box1">
    32. <div class="box2">
    33. <div class="box3">div>
    34. div>
    35. div>
    36. <script>
    37. var box1 = document.querySelector('.box1');
    38. var box2 = document.querySelector('.box2');
    39. var box3 = document.querySelector('.box3');
    40. box1.onclick = function(){
    41. console.log('box111111');
    42. }
    43. script>

     分析:只给box1绑定点击事件,点击box1的区域,包括box2和box3(都在box1的区域内),都会打印box111111

    利用绝对定位把box2和box3放到box1区域红色区域外面去,再点击box2和box3会出现什么效果?

    1. <style>
    2. *{margin: 0;}
    3. .box1{
    4. width: 400px;
    5. height: 300px;
    6. background-color: brown;
    7. cursor: pointer;
    8. position:relative;
    9. left: 100px;
    10. top: 20px;
    11. }
    12. .box2{
    13. width: 200px;
    14. height: 200px;
    15. background-color: red;
    16. cursor: pointer;
    17. /* 让box2带着box3从box1的区域出去 */
    18. position:absolute;
    19. left: 500px;
    20. top: 20px;
    21. }
    22. .box3{
    23. width: 100px;
    24. height: 100px;
    25. background-color: gold;
    26. cursor: pointer;
    27. margin: 10px;
    28. padding: 5px;
    29. border: 3px solid saddlebrown;
    30. }
    31. style>
    32. <div class="box1">
    33. <div class="box2">
    34. <div class="box3">div>
    35. div>
    36. div>
    37. <script>
    38. var box1 = document.querySelector('.box1');
    39. var box2 = document.querySelector('.box2');
    40. var box3 = document.querySelector('.box3');
    41. box1.onclick = function(){
    42. console.log('box111111');
    43. }
    44. script>

     分析:只给了box1绑定点击事件,然后把box2和box3移到box1外面的区域,为什么点击box2和box3还是会打印box111111。原因是box2是box1的子元素,box3又是box2的子元素,虽然通过绝对定位移出去了,但是还是box1的子元素。所以我们要研究事件是怎么执行的,跟父子关系有什么联系?

    事件对象的path属性:

    1. <style>
    2. *{margin: 0;}
    3. .box1{
    4. width: 400px;
    5. height: 300px;
    6. background-color: brown;
    7. cursor: pointer;
    8. position:relative;
    9. left: 100px;
    10. top: 20px;
    11. }
    12. .box2{
    13. width: 200px;
    14. height: 200px;
    15. background-color: red;
    16. cursor: pointer;
    17. /* 让box2带着box3从box1的区域出去 */
    18. position:absolute;
    19. left: 500px;
    20. top: 20px;
    21. }
    22. .box3{
    23. width: 100px;
    24. height: 100px;
    25. background-color: gold;
    26. cursor: pointer;
    27. margin: 10px;
    28. padding: 5px;
    29. border: 3px solid saddlebrown;
    30. }
    31. style>
    32. <div class="box1">
    33. <div class="box2">
    34. <div class="box3">div>
    35. div>
    36. div>
    37. <script>
    38. var box1 = document.querySelector('.box1');
    39. var box2 = document.querySelector('.box2');
    40. var box3 = document.querySelector('.box3');
    41. box1.onclick = function(e){
    42. //e是事件对象
    43. console.log('box111111',e);
    44. }
    45. script>

    分析:点击了box3,触发了box1的点击事件,生成的事件对象,然后查看其中的path属性 

     就是点击事件所经过的层级(嵌套关系)。

     但通常,一个事件会从父元素开始向目标元素传播(捕获),然后它将被传播回父元素(冒泡)。这个过程就是事件流/事件链.

    2.事件流

    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,直接就不会执行点击事件。 

    3.证明事件执行顺序是按事件流来的

    1. <style>
    2. *{margin: 0;}
    3. .box1{
    4. width: 400px;
    5. height: 300px;
    6. background-color: brown;
    7. cursor: pointer;
    8. position:relative;
    9. left: 100px;
    10. top: 20px;
    11. }
    12. .box2{
    13. width: 200px;
    14. height: 200px;
    15. background-color: red;
    16. cursor: pointer;
    17. /* 让box2带着box3从box1的区域出去 */
    18. position:absolute;
    19. left: 500px;
    20. top: 20px;
    21. }
    22. .box3{
    23. width: 100px;
    24. height: 100px;
    25. background-color: gold;
    26. cursor: pointer;
    27. margin: 10px;
    28. padding: 5px;
    29. border: 3px solid saddlebrown;
    30. }
    31. style>
    32. <div class="box1">
    33. <div class="box2">
    34. <div class="box3">div>
    35. div>
    36. div>
    37. <script>
    38. var box1 = document.querySelector('.box1');
    39. var box2 = document.querySelector('.box2');
    40. var box3 = document.querySelector('.box3');
    41. box1.onclick = function(e){
    42. //e是事件对象
    43. console.log('box111111',e);
    44. }
    45. box2.onclick = function(e){
    46. //e是事件对象
    47. console.log('box222222',e);
    48. }
    49. box3.onclick = function(e){
    50. //e是事件对象
    51. console.log('box333333',e);
    52. }
    53. script>

    分析:

    点击box1,打印box111111 PointerEvent {}

    点击box2:打印

    box22222 PointerEvent {}

    box111111  PointerEvent {}

    点击box3:打印

    box33333 PointerEvent {}

    box22222 PointerEvent {}

    box111111  PointerEvent {}

     结合事件流,就可以很容易理解这个打印结果。

    3.addEventListener的第三个参数

    在我们平常用的addEventListener方法中,一般只会用到两个参数,一个是需要绑定的事件另一个是触发事件后要执行的函数,然而,addEventListener还可以传入第三个参数

    第三个参数默认是false,也就是第三个参数不写或者写false: 表示在事件冒泡阶段调用事件处理函数;如果参数为true,则表示在事件捕获阶段调用处理函数

    1. //给box1绑定两个事件,一个捕获阶段触发,一个冒泡阶段触发
    2. box1.addEventListener('click',(e)=>{
    3. console.log('box111111bbbbbbb',e);
    4. },true)
    5. box1.addEventListener('click',(e)=>{
    6. console.log('box111111bbbbbbb',e);
    7. },false)

    问题: addEventListener的第三个参数为true是阻止事件传递还是false是阻止事件传递?

    答案:都不会阻止事件传递,因为true捕获阶段触发,false冒泡阶段触发

    要阻止事件传递,唯一的方式就是阻止事件冒泡,事件对象调用stopPropagation();

    4.阻止事件冒泡和默认事件

    1.event.stopPropagation()方法
    这是阻止事件的冒泡方法,不让事件向documen上蔓延,但是默认事件任然会执行,当你掉用这个方法的时候,如果点击一个连接,这个连接仍然会被打开,
    2.event.preventDefault()方法
    这是阻止默认事件的方法,调用此方法是,连接不会被打开,但是会发生冒泡,冒泡会传递到上一层的父元素;

    1. <style>
    2. .box1{
    3. width: 200px;
    4. height: 200px;
    5. background-color: blue;
    6. }
    7. .box2{
    8. width: 100px;
    9. height: 100px;
    10. background-color: pink;
    11. }
    12. style>
    13. <div class="box1">
    14. <div class="box2">
    15. <a href="https://www.baidu.com" id="a">跳到百度a>
    16. div>
    17. div>
    18. <script>
    19. var box1 = document.querySelector('.box1');
    20. var a = document.querySelector('#a');
    21. box1.addEventListener('click',(e)=>{
    22. console.log('box1111111');
    23. })
    24. a.addEventListener('click',(e)=>{
    25. console.log(666);
    26. //stopPropagation 阻止冒泡 但不阻止默认行为
    27. e.stopPropagation();
    28. //阻止默认行为,但不阻止冒泡 所以要想阻止默认行为,又要阻止冒泡,两个都写
    29. e.preventDefault();
    30. })
    31. 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()     阻止事件冒泡 也阻止同一类型的其它事件程序从它这里过(同一类型的冒泡),阻止不了默认事件这些哈!!

    1. <style>
    2. *{margin: 0;}
    3. .box1{
    4. width: 400px;
    5. height: 300px;
    6. background-color: brown;
    7. cursor: pointer;
    8. position:relative;
    9. left: 100px;
    10. top: 20px;
    11. }
    12. .box2{
    13. width: 200px;
    14. height: 200px;
    15. background-color: red;
    16. cursor: pointer;
    17. /* 让box2带着box3从box1的区域出去 */
    18. position:absolute;
    19. left: 500px;
    20. top: 20px;
    21. }
    22. .box3{
    23. width: 100px;
    24. height: 100px;
    25. background-color: gold;
    26. cursor: pointer;
    27. margin: 10px;
    28. padding: 5px;
    29. border: 3px solid saddlebrown;
    30. }
    31. style>
    32. <div class="box1">
    33. <div class="box2">
    34. <div class="box3">div>
    35. div>
    36. div>
    37. <script>
    38. var box1 = document.querySelector('.box1');
    39. var box2 = document.querySelector('.box2');
    40. var box3 = document.querySelector('.box3');
    41. box1.addEventListener('click',(e)=>{
    42. console.log('box111111',e);
    43. })
    44. box3.addEventListener('click',(e)=>{
    45. console.log('box3333aaaaaa',e);
    46. e.stopPropagation();
    47. // e.stopImmediatePropagation();
    48. })
    49. box3.addEventListener('click',(e)=>{
    50. console.log('box3333bbbbb',e);
    51. })
    52. script>

    分析:点击box3时,是阻止冒泡的情况下:e.stopPropagation();打印出

    box3333aaaaaa     PointerEvent{}

    box3333bbbbbb     PointerEvent{}    还好,虽然阻止冒泡,但至少本身其它的同一类型事件还能过。

    点击box3时,阻止事件冒泡 也阻止同一类型的其它事件程序从它这里过 情况下 :e.stopImmediatePropagation();打印出

    box3333aaaaaa    PointerEvent{}

    e.stopImmediatePropagation();   就连本身绑定同一类型的第二个点击事件都经过不了,更不说冒泡了。


    5.阻止事件冒泡和阻止事件默认情况的兼容性写法

    1. function(e){
    2. //得到事件对象
    3. var e=e||window.event;
    4. e.stopPropagation();//阻止冒泡
    5. // 兼容处理
    6. if (e.stopPropagation) {
    7. e.stopPropagation();
    8. }else{
    9. // IE浏览器
    10. e.cancelBubble=true;
    11. }
    12. }
    1. function(e){
    2. var e=e||window.event;
    3. e.defaultPrevent();//默认事件
    4. if (e.preventDefault) {
    5.    //w3c
    6. e.preventDefault();
    7. } else{
    8.    //ie
    9. e.returnValue = false;
    10. }
    11. }

    5.易错点

    1.阻止冒泡event.stopPropagation(),和阻止冒泡和程序经过stopImmediatePropagation() 都是针对的同一类型的事件。如果是两个不同类型,则并不能阻止另外一个。

    1. <style>
    2. *{margin: 0;}
    3. .box1{
    4. width: 400px;
    5. height: 300px;
    6. background-color: brown;
    7. cursor: pointer;
    8. position:relative;
    9. left: 100px;
    10. top: 20px;
    11. }
    12. .box2{
    13. width: 200px;
    14. height: 200px;
    15. background-color: red;
    16. cursor: pointer;
    17. /* 让box2带着box3从box1的区域出去 */
    18. position:absolute;
    19. left: 500px;
    20. top: 20px;
    21. }
    22. .box3{
    23. width: 100px;
    24. height: 100px;
    25. background-color: gold;
    26. cursor: pointer;
    27. margin: 10px;
    28. padding: 5px;
    29. border: 3px solid saddlebrown;
    30. }
    31. style>
    32. <div class="box1">
    33. <div class="box2">
    34. <div class="box3">div>
    35. div>
    36. div>
    37. <script>
    38. var box1 = document.querySelector('.box1');
    39. var box2 = document.querySelector('.box2');
    40. var box3 = document.querySelector('.box3');
    41. box1.addEventListener('click',(e)=>{
    42. console.log('box111111',e);
    43. })
    44. box2.addEventListener('mousedown',(e)=>{
    45. console.log('box22222',e);
    46. })
    47. box3.addEventListener('click',(e)=>{
    48. console.log('box3333aaaaaa',e);
    49. // e.stopPropagation();
    50. e.stopImmediatePropagation();
    51. })
    52. box3.addEventListener('click',(e)=>{
    53. console.log('box3333bbbbb',e);
    54. })
    55. 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事件冒泡

     

  • 相关阅读:
    【Java】PAT(Basic Level) Practice(中文) 1015德才论
    简单解析hyperf-TCP-RPC-Json请求的数据结构
    局部最小值问题
    低代码如何提升开发人员的开发能力
    高防服务器和普通服务器之间的区别
    List集合
    简单好用的 SemVer: 如何命名你的应用版本
    Docker 服务器配置证书后,客户端连接
    利用信号量semaphore实现两个进程读写同步 Linux C
    虚拟人三维动画宣传片案例分享 | 广州“五羊”城市文化IP商业体裸眼3D广告影片
  • 原文地址:https://blog.csdn.net/weixin_47075145/article/details/125899950