事件冒泡、事件捕获、DOM0级事件处理程序、DOM2级事件处理程序

2,424 阅读8分钟

前言:

这篇文章起源于上次工作上的原因,在事件上出的bug,所以就抽空写出一篇,也便于自己以后查阅,如若有幸被您阅读的话,小妹备感荣幸,文章仅为个人理解,如果内容有误的还望海涵,在您时间还方便的时候,希望能告知小妹!谢谢!

什么是事件?

事件是文档和浏览器窗口中发生的特定的交互瞬间。 事件是javascript应用跳动的心脏,也是把所有东西黏在一起的胶水,当我们与浏览器中web页面进行某些类型的交互时,事件就发生了。

事件可能是用户在某些内容上的点击,鼠标经过某个特定元素或按下键盘上的某些按键,事件还可能是web浏览器中发生的事情,比如说某个web页面加载完成,或者是用户滚动窗口或改变窗口大小。


什么是事件流:

事件流描述的是从页面中接受事件的顺序,但有意思的是,微软(IE)和网景(Netscape)开发团队居然提出了两个截然相反的事件流概念,IE的事件流是事件冒泡流(event bubbling),而Netscape的事件流是事件捕获流(event capturing)

事件冒泡

IE提出的事件流叫做事件冒泡,即事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点,看一下以下示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body onclick="bodyClick()">

    <div onclick="divClick()">
        <button onclick="btn()">
            <p onclick="p()">点击冒泡</p>
        </button>
    </div>
    <script>
       
       function p(){
          console.log('p标签被点击')
       }
        function btn(){
            console.log("button被点击")
        }
         function divClick(event){
             console.log('div被点击');
         }
        function bodyClick(){
            console.log('body被点击')
        }

    </script>

</body>
</html>

接下来我们点击一下页面上的p元素,看看会发生什么:


正如上面我们所说的,它会从一个最具体的的元素接收,然后逐级向上传播,p=>button=>div=>body..........事件冒泡可以形象地比喻为把一颗石头投入水中,泡泡会一直从水底冒出水面。

事件捕获

网景公司提出的事件流叫事件捕获流。

事件捕获流的思想是不太具体的DOM节点应该更早接收到事件,而最具体的节点应该最后接收到事件,针对上面同样的例子,点击按钮,那么此时click事件会按照这样传播:(下面我们就借用addEventListener的第三个参数来模拟事件捕获流)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<div>
    <button>
        <p>点击捕获</p>
    </button>
</div>
<script>
    let oP=document.querySelector('p');
    let oB=document.querySelector('button');
    let oD=document.querySelector('div');
    let oBody=document.querySelector('body');

    oP.addEventListener('click',function(){
        console.log('p标签被点击')
    },true);

    oB.addEventListener('click',function(){
        console.log("button被点击")
    },true);

    oD.addEventListener('click',  function(){
        console.log('div被点击')
    },true);

    oBody.addEventListener('click',function(){
        console.log('body被点击')
    },true);

</script>
</body>
</html>

同样我们看一下后台的打印结果:


正如我们看到的,和冒泡流万全相反,从最不具体的元素接收到最具体的元素接收事件 body=>div=>button=>p

DOM 0级事件处理程序:

DOM 0级事件规定的事件流包含3个阶段,事件捕获阶段、处于目标阶段、事件冒泡阶段。首先发生的事件捕获为截获事件提供机会,然后是实际的目标接收事件,最后一个阶段是事件冒泡阶段,可以在这个阶段对事件做出响应。

DOM事件流中,事件的目标在捕获阶段不会接收到事件,这意味着在捕获阶段事件从document<p>就停止了,下个阶段是处于目标阶段,于是事件在<p>上发生,并在事件处理中被看成冒泡阶段的一部分,然后,冒泡阶段发生,事件又传播回document。

下面是我们模拟它的示例:


 
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
    <body>
        <button id="btn">DOM事件流</button>
<script>

    let btn=document.getElementById("btn");
    
    btn.onclick=function(event){
        console.log("div 处于目标阶段");
    };
    
    document.body.addEventListener("click",function(event){
        console.log("event bubble 事件冒泡");
    },false);

    document.body.addEventListener("click",function(event){
        console.log("event catch 事件捕获");
    },true);

</script>
</body>
</html>

看看后台给出什么结果:


整体流程如下:就是这样一个流程,先捕获,然后处理,然后再冒泡出去。

关于DOM 2级事件处理程序:

DOM 2级事件定义了两方法:用于处理添加事件和删除事件的操作:添加事件 addEventListener() 删除事件 removeEventListener()

所有DOM节点中都包含这两个方法,并且他们都包含3个参数:

  • (1) 要处理的事件方式(例如:click,mouseover,dbclick.....)
  • (2)事件处理的函数,可以为匿名函数,也可以为命名函数(但如果需要删除事件,必须是命名函数)
  • (3)一个布尔值,代表是处于事件冒泡阶段处理还是事件捕获阶段(true:表示在捕获阶段调用事件处理程序;false:表示在冒泡阶段调用事件处理程序)

    使用DOM2级事件处理程序的主要好处是可以添加多个事件处理程序,事件处理会按照他们的顺序触发,通过addEventListener添加的事件只能用removeEventListener来移除,移除时传入的参数与添加时使用的参数必须相同,这也意味着添加的匿名函数将无法移除,(注意:我们默认的第三个参数都是默认false,是指在冒泡阶段添加,大多数情况下,都是将事件处理程序添加到事件的冒泡阶段,这样可以最大限度的兼容各个浏览器)

    //这是一个DOM 2级事件 添加一个简单的事件(此时添加的是一个匿名函数)
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    
        <button>按钮</button>
        <script>
            let btn=document.querySelector('button');
            btn.addEventListener('click',function(){
                console.log('我是按钮')
            },false)   //当第三个参数不写时,也是默认为false(冒泡时添加事件)
        </script>
    
    </body>
    </html>
    
    

    那么我们试试命名函数的写法:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    
        <button>按钮</button>
        <script>
            let btn=document.querySelector('button');
            btn.addEventListener('click',foo,false);
            function foo(){
                console.log('我是按钮')
            }
               //其实操作就是把写在里面的函数拿到了外面,而在原来的位置用函数名来代替
        </script>
    </body>
    </html>
    

    那么我们添加两个事件怎么写呢?

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    
        <button>按钮</button>
        <script>
            let btn=document.querySelector('button');
             //第一个事件
            btn.addEventListener('click',foo,false);
            function foo(){
                console.log('我是按钮')
            }
            //第二个事件
            btn.addEventListener('click',newFoo,false);
            function newFoo(){
                console.log('我是新按钮')
            }
        </script>
    </body>
    </html>
    
    

    那么我们看看这两个事件的执行顺序是怎样的:



    所以说,我们可以使用addEventListener添加多个事件,事件的顺序就是按照我们程序写的顺序执行的。

    那我们试试DOM 0级事件处理程序,是如何处理添加两个事件后的执行的

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <button onclick="foo()"  onclick="newFoo()">按钮</button>
        <script>
            function foo(){
                console.log(2)
            }
            function newFoo(){
                console.log(9)
            }
        </script>
    </body>
    </html>
    

    看一下结果:



    结果只执行了第一个事件,第二个被忽略,所以当我们只添加一个事件时,可以使用DOM0级事件处理程序来执行,而当我们的需求是多个事件时,我们就需要用到addEventLiener去执行。

    未完待续:

    以上就是事件冒泡,事件捕获,DOM事件流,DOM 2级事件流的内容,但是还有没有叙述完整,本人会在这1-2天内继续补充,敬请期待!谢谢阅读!

    接下来我还会持续追加,看文章的小伙伴们可以添加一下关注哦!

        作者:晴天de雨滴    
        出处:https://juejin.cn/post/6844903518902583310
        版权所有,欢迎保留原文链接进行转载:) 
    

    如果你对我对文章感兴趣或者有些建议想说给我听👂,也可以添加一下微信哦!

    如果亲感觉我的文章还不错的话,可以一下添加关注哦!

    最后:
            祝各位工作顺利!
                            -小菜鸟Christine