你不知道的javascript事件

事件流描述的是从页面中接受事件的顺序。然而ie和netscape分别提出了彻底相反的的概念:事件冒泡和事件捕获。下面就说说这两种事件流:html

事件冒泡

事件冒泡,就是说时间开始时由具体的元素接受,而后逐级向上传播到较为不具体的节点。看看下面的图就比较清楚了:
图片描述浏览器

好比说在图中的<div>元素中添加一个click事件,那么这个<div>元素就是咱们的单击事件,而后click事件就会沿DOM树向上传播,在每一级节点上都会发生,直到传播到document对象。看看下面的实例:函数

在理解实例事前先说说实例用到的核心操做:addEventListener(),这是DOM2事件定义的方法,用于处理添加指定事件处理程序的操做,有添加的方法固然也就会有删除事件处理程序的方法removeEventListener()。全部的DOM节点中包含了这两个方法,它们都接受3个参数:要处理的事件名、做为事件处理程序的函数和一个布尔值。若是布尔值参数是false,表示在冒泡阶段调用事件处理程序;若是是true,表示在捕获阶段调用事件处理程序(稍后会说到事件捕获),布尔值默认参数是false。flex

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        html, body {
            height: 100%;
        }

        body{
            display: flex;
            justify-content: center;
            align-items: center;
        }
        .box{
            width: 200px;
            height: 200px;
            background-color: green;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .inner{
            width: 100px;
            height: 100px;
            background-color: blue;
            align-self: flex-end;
        }
        .box3{
            width: 50px;
            height: 50px;
            background-color: red;
            align-self: flex-end;
        }
    </style>
</head>
<body>
    <div class="box"> 外部盒
        <div class="inner">inner
            <div class="box3">box3
            </div>
        </div>
    </div>
    <script>
        var oBox = document.querySelector('.box');
        var oInner = document.querySelector('.inner');
        oBox.addEventListener('click',function(e){
            console.log('点击了外部盒子');
        },false);
        oInner.addEventListener('click',function(e){
            console.log('点击了inner');
        },false);
        var box3 = document.querySelector('.box3');
        box3.addEventListener('click',function(e){
            console.log('点击了box3');
        },false);  
    </script>
</body>
</html>

浏览器效果:
图片描述ui

如今就说说实例中触发事件的执行顺序,挺有意思的,容易被绕进去:spa

  • 点击绿色区域code

图片描述

输出的值也就是一个了。htm

  • 点击蓝色区域对象

图片描述

输出的值有两个,这个为何呢,缘由很简单,蓝色区域是在绿色区域内,蓝色区域触发click事件,在冒泡阶段调用事件处理程序,点击蓝色区域,click事件沿DOM树向上传播,由类属性为inner的元素的向上传播到类属性为box的元素,再依次向上传播,故输出的值为两个。注意其输出的顺序:先输出自身元素触发事件中的值,而后再是父元素事件中的值。由内向外,这也就是事件冒泡的核心内容了。事件

  • 点击红色区域

图片描述

输出的值有三个,缘由就再也不解释了,缘由和上面的同样,同时也要注意下它们的输出顺序。经过这个实例对事件冒泡有了解了,接下来咱们来深刻下:

在咱们实际开发中,是很不肯意事件在DOM层次中的传播,很影响体验效果,如上实例,触发红色区域的元素,我只想输出一个值,然而恰恰输出了三个值,是否是很烦啊???这个仍是有解决方案的,stopPropagation()方法用于当即中止事件在DOM层次中的传播,即取消进一步的事件冒泡或捕获。看下面的例子(html代码与上面实例同样):

  1. 红色区域中止传播

<script>
        var oBox = document.querySelector('.box');
        var oInner = document.querySelector('.inner');
        oBox.addEventListener('click',function(event){
            console.log('点击了外部盒子');
        },false);
        oInner.addEventListener('click',function(event){
            console.log('点击了inner'); 
        },false);
        var box3 = document.querySelector('.box3');
        box3.addEventListener('click',function(event){
            event.stopPropagation();
            console.log('点击了box3');
        },false);  
    </script>

先说说上面的代码中涉及到的一个局部变量event,相信不少人对这个变量都不是很理解,我刚接触的时候也不是很理解这个变量,为何会有这个变量,这个变量干吗用的,怎么还能够调用方法和属性,好神奇啊!!!如今我就来讲说这个局部变量,这个变量就是事件对象,在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含了全部与事件有关的信息,包括了致使事件的元素、事件的类型以及其它与特定事件相关的信息。例如,鼠标操做致使的事件对象中,会包含鼠标位置的信息。

  • 点击红色区域:

图片描述

在红色区域的click事件中加了stopPropagation(),这就当即中止了事件在DOM层次中的传播,也就是取消进一步的事件冒泡,输出的值也就只有一个了。

  • 分别点击蓝色区域、绿色区域:

图片描述图片描述

从输出能够看出,这两部分没有加stopPropagation(),事件依旧在DOM层次中的传播,同时注意它们的输出顺序。

2.蓝色区域中止传播
<script>
        var oBox = document.querySelector('.box');
        var oInner = document.querySelector('.inner');
        oBox.addEventListener('click',function(event){
            console.log('点击了外部盒子');
        },false);
        oInner.addEventListener('click',function(event){
            event.stopPropagation();
            console.log('点击了inner'); 
        },false);
        var box3 = document.querySelector('.box3');
        box3.addEventListener('click',function(event){
            console.log('点击了box3');
        },false);  
    </script>
  • 点击蓝色区域

图片描述

成功取消了事件冒泡,输出的值是一个即自己触发输出,而不是两个值。

  • 点击红色区域

图片描述

这个输出了两个值,不是三个值,或许会感到很好奇,我取消的是蓝色区域的事件捕获,为何click红色区域只输出两个,其实正由于你在蓝色区域内调用了stopPropagation()方法,其事件就会被中止冒泡,即便你点击的是红色区域,事件冒泡最终停留在蓝色区域,传播不到绿色区域,即输出的值为两个。

  • 点击绿色区域

图片描述

由于绿色区域是三个颜色区域最外层区域,其子元素的取消事件冒泡对本身没有影响,再者其父元素没有作任何事件监听,故输出的值仅仅是其自身事件触发获得。

3.绿色区域中止传播
<script>
        var oBox = document.querySelector('.box');
        var oInner = document.querySelector('.inner');
        oBox.addEventListener('click',function(event){
            event.stopPropagation();
            console.log('点击了外部盒子');
        },false);
        oInner.addEventListener('click',function(event){
            
            console.log('点击了inner'); 
        },false);
        var box3 = document.querySelector('.box3');
        box3.addEventListener('click',function(event){    
            console.log('点击了box3');
        },false);  
    </script>
  • 分别点击绿色区域,蓝色区域,红色区域:

图片描述图片描述图片描述

获得的效果和没有作中止事件在DOM层中的传播是同样的,由于绿色区域是三个颜色区域最外层区域,其作取消事件冒泡,其子元素的事件冒泡对此没有影响,再者其父元素没有作任何事件监听,故输出的值和没有作中止事件在DOM层中的传播。

事件捕获

事件捕获是不太具体的节点应该更早接受到事件,而具体的节点应该最后接受到事件。看看下面的图:
图片描述

如图中,div元素增长一个click事件,在事件捕获中,document对象首先接受到click事件,而后事件沿DOM树依次向下,一直传播到事件的实际目标,即div元素。看看下面的实例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        html, body {
            height: 100%;
        }

        body{
            display: flex;
            justify-content: center;
            align-items: center;
        }
        .box{
            width: 200px;
            height: 200px;
            background-color: green;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .inner{
            width: 100px;
            height: 100px;
            background-color: blue;
            align-self: flex-end;
        }
        .box3{
            width: 50px;
            height: 50px;
            background-color: red;
            align-self: flex-end;
        }
    </style>
</head>
<body>
    <div class="box"> 外部盒
        <div class="inner">inner
            <div class="box3">box3
            </div>
        </div>
    </div>
    <script>
        var oBox = document.querySelector('.box');
        var oInner = document.querySelector('.inner');
        oBox.addEventListener('click',function(event){
            console.log('点击了外部盒子');
        },true);
        oInner.addEventListener('click',function(event){
            
            console.log('点击了inner'); 
        },true);
        var box3 = document.querySelector('.box3');
        box3.addEventListener('click',function(event){    
            console.log('点击了box3');
        },true);  
    </script>
</body>
</html>

这份代码与冒泡事件的代码大体相同,有变化的就是添加事件处理程序的操做addEventListener(),其中的第三个参数改成了true,表示在捕获阶段调用事件处理程序。

  • 分别点击绿色区域、蓝色区域、红色区域时执行顺序:

图片描述图片描述图片描述

输出的缘由相信你们都很清楚了,在这就再也不解释了,须要注意的是控制台值得输出顺序,和冒泡事件输出顺序相反的。

接下来再讲讲捕获事件中中止事件在DOM层次中的传播,取消进一步的事件捕获,这和冒泡事件的很不同,主要缘由在于它们的传播顺序恰好相反,你们须要认真的思考,不要搞混淆了,如今就讲讲其中的一种比较容易出错的,其它的你们能够自行思考,完整代码已经贴出来了,你们看看下面的实例:

<script>
        var oBox = document.querySelector('.box');
        var oInner = document.querySelector('.inner');
        oBox.addEventListener('click',function(event){
            console.log('点击了外部盒子');
        },true);
        oInner.addEventListener('click',function(event){
            event.stopPropagation();
            console.log('点击了inner'); 
        },true);
        var box3 = document.querySelector('.box3');
        box3.addEventListener('click',function(event){    
            console.log('点击了box3');
        },true);  
 </script>

蓝色区域的click事件中添加了stopPropagation()方法,取消进一步的事件捕获,分别点击不一样区域,会产生什么效果呢???你们往下看:

  • 点击绿色区域

图片描述

这个缘由就再也不解释了,你们都懂!!!

  • 点击蓝色区域

图片描述

输出的值有两个,这个你们容易搞错,在蓝色区域的click事件中添加了stopPropagation()方法,取消了事件捕获,不该该只出现“点击了inner”的吗,怎么还出现了“点击了外部盒子”呢???这就又可能和事件冒泡搞混淆了,事件捕获是由不太具体的节点应该更早接受到事件,而具体的节点应该最后接受到事件。简单点来讲是由外向内传播。蓝色区域的click事件中添加了stopPropagation()方法,只是该节点不会再向下传播,该节点的父元素仍是会传播到该节点的,因此输出的值会有两个,而不是一个。

  • 点击红色区域

图片描述

输出的值有两个,这个的缘由和上面的差很少,思想是同样的,蓝色区域的click事件中添加了stopPropagation()方法,该节点不会再向下传播,点击红色区域,捕获事件只能作到蓝色区域就中止了。

事件流

“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。首先发生的是事件捕获阶段,为截获事件提供了机会。而后是实际的目标接收事件。最后一个阶段是冒泡阶段,能够在这个阶段对事件作出响应。
图片描述

在DOM事件流中,实际的目标如div元素在捕获阶段不会接受到事件,这也就意味在捕获阶段,事件从document到html元素再到body后就中止了。下一阶段是处于目标阶段,因而事件在div上发生,并在事件处理中被当作冒泡阶段的一部分,而后,冒泡阶段发生,事件有传播会文档。

<script>
        var oBox = document.querySelector('.box');
        var oInner = document.querySelector('.inner');
        oBox.addEventListener('click',function(event){
            console.log('点击了外部盒子');
        },false);
        oInner.addEventListener('click',function(event){
            console.log('点击了inner'); 
        },true);
        var box3 = document.querySelector('.box3');
        box3.addEventListener('click',function(event){    
            console.log('点击了box3');
        },true);  
    </script>
  • 分别点击绿色区域、蓝色区域、红色区域

图片描述图片描述图片描述

具体缘由就在过多的去解释了,这个就留给你们思考,清楚的的理解事件流机制就能很快的得出答案。
何处调用事件处理程序,什么时候在冒泡阶段调用事件处理程序,什么时候在捕获阶段调用事件处理程序。

结束

  • 终于写完了!