Javascript捕获阶段和冒泡阶段(DOM2级事件,firefox,chrome,ie9,safari)

DOM2级事件流规定的事件包括三个阶段:事件捕获阶段、处于目标阶段和事件冒泡阶段。

首先发生的是事件捕获。然后是实际的目标接收到事件。最后一个阶段是冒泡阶段。

addEventListener第一个参数是事件名,不带on前缀,第二个参数是绑定的函数,这个函数带有唯一的参数event,第三个参数为isCapture,是否捕获阶段执行的含义。当设置为false时在冒泡阶段执行,是我们常用的方式。因为我们可以对内层DIV绑定的事件函数中,执行event.stopPropagation()来阻止事件继续传递。

给一段JS示例代码:

<!DOCTYPE html>
<html>
<head>
    <title>js测试</title>
</head>
<body>
<div id="outer">
    外层DIV
    <div id="inner">内层DIV</div>
</div>
<script>
    var outer = document.getElementById('outer');
    var inner = document.getElementById('inner');

    /**
     * addEventListener 为Dom元素绑定事件
     * 参数1:事件名称(不以on开头)
     * 参数2:事件执行的函数
     * 参数3:是否按捕获阶段执行,一般设置为false,冒泡阶段执行
     * 说明:
     * DOM2级事件规定的事件流包括三个阶段。
     * 第一个阶段是事件捕获阶段,从外往内传递;
     * 第二个阶段是处于目标阶段,这个阶段中参数3设置false或true都一样,按照绑定先后顺序执行。
     * 第三个阶段是冒泡阶段,从内往外传递。
     * 注意:
     * 以下的注释都针对点击一次“内层DIV”
     */
    outer.addEventListener('click', function(event){
        console.log('outer clicked! false 1'); // 最后执行,因为处于最外层的冒泡阶段,但如果点击“外层DIV”则最先执行
    }, false);
    outer.addEventListener('click', function(event){
        console.log('outer clicked! true 2'); // 最先执行因为处于外层捕获阶段,但如果点击“外层DIV”则第2执行
    }, true);

    
    inner.addEventListener('click', function(event){
        console.log('inner clicked! false 1'); // 第2执行,因为是目标
    }, false);
    inner.addEventListener('click', function(event){
        console.log('inner clicked! true 2'); // 第3执行,因为是目标
    }, true);
    inner.addEventListener('click', function(event){
        console.log('inner clicked! false 3'); // 第4执行,因为是目标
    }, false);
</script>
</body>
</html>

 在上述代码中,打开控制台,如果点击“内层DIV”文字,会得到如下执行结果:


addEventListener 函数第三个参数设置为false是最常用的,绑定的函数将在冒泡阶段执行,设置为true则是在捕获阶段执行,但如果事件发生时,该DOM元素是目标元素,那么绑定函数则是在目标阶段执行,这时设置的true或false就毫无意义了,函数会按照绑定先后顺序执行。

这里可以看到,最外层DIV在捕获阶段的事件绑定函数最先得到执行。

然后是目标DIV按照先后顺序绑定的事件函数(跟第三个参数为false还是true毫无关系)。

最后才是最外层DIV的冒泡阶段绑定函数。

如果点击“外层DIV”,则得到如下结果:



 这是执行顺序则是按绑定顺序来的,因为这时候两个函数已经不是针对冒泡阶段或捕获阶段了,都是目标阶段执行的。

事件传递顺序是: window -- document -- html -- body -- div ...

我们给代码增加window绑定事件,将看到,window处于最外层,捕获阶段最先执行,冒泡阶段最后执行。

<!DOCTYPE html>
<html>
<head>
    <title>js测试</title>
</head>
<body>
<div id="outer">
    外层DIV
    <div id="inner">内层DIV</div>
</div>
<script>
    var outer = document.getElementById('outer');
    var inner = document.getElementById('inner');

    /**
     * addEventListener 为Dom元素绑定事件
     * 参数1:事件名称(不以on开头)
     * 参数2:事件执行的函数
     * 参数3:是否按捕获阶段执行,一般设置为false,冒泡阶段执行
     * 说明:
     * DOM2级事件规定的事件流包括三个阶段。
     * 第一个阶段是事件捕获阶段,从外往内传递;
     * 第二个阶段是处于目标阶段,这个阶段中参数3设置false或true都一样,按照绑定先后顺序执行。
     * 第三个阶段是冒泡阶段,从内往外传递。
     * 注意:
     * 以下的注释都针对点击一次“内层DIV”
     */
    outer.addEventListener('click', function(event){
        console.log('outer clicked! false 1'); // 最后执行,因为处于最外层的冒泡阶段,但如果点击“外层DIV”则最先执行
    }, false);
    outer.addEventListener('click', function(event){
        console.log('outer clicked! true 2'); // 最先执行因为处于外层捕获阶段,但如果点击“外层DIV”则第2执行
    }, true);

    
    inner.addEventListener('click', function(event){
        console.log('inner clicked! false 1'); // 第2执行,因为是目标
    }, false);
    inner.addEventListener('click', function(event){
        console.log('inner clicked! true 2'); // 第3执行,因为是目标
    }, true);
    inner.addEventListener('click', function(event){
        console.log('inner clicked! false 3'); // 第4执行,因为是目标
    }, false);
    
    window.addEventListener('click', function(event){
        console.log('window clicked! false');
    }, false);
    window.addEventListener('click', function(event){
        console.log('window clicked! true');
    }, true);
</script>
</body>
</html>

 点击“内层DIV”,可以得到如下结果:



 

如果我们在window的捕获阶段事件绑定函数中增加event.stopPropagation();那么事件被扼杀在这个阶段。代码如下:

<!DOCTYPE html>
<html>
<head>
    <title>js测试</title>
</head>
<body>
<div id="outer">
    外层DIV
    <div id="inner">内层DIV</div>
</div>
<script>
    var outer = document.getElementById('outer');
    var inner = document.getElementById('inner');

    /**
     * addEventListener 为Dom元素绑定事件
     * 参数1:事件名称(不以on开头)
     * 参数2:事件执行的函数
     * 参数3:是否按捕获阶段执行,一般设置为false,冒泡阶段执行
     * 说明:
     * DOM2级事件规定的事件流包括三个阶段。
     * 第一个阶段是事件捕获阶段,从外往内传递;
     * 第二个阶段是处于目标阶段,这个阶段中参数3设置false或true都一样,按照绑定先后顺序执行。
     * 第三个阶段是冒泡阶段,从内往外传递。
     * 注意:
     * 以下的注释都针对点击一次“内层DIV”
     */
    outer.addEventListener('click', function(event){
        console.log('outer clicked! false 1'); // 最后执行,因为处于最外层的冒泡阶段,但如果点击“外层DIV”则最先执行
    }, false);
    outer.addEventListener('click', function(event){
        console.log('outer clicked! true 2'); // 最先执行因为处于外层捕获阶段,但如果点击“外层DIV”则第2执行
    }, true);

    
    inner.addEventListener('click', function(event){
        console.log('inner clicked! false 1'); // 第2执行,因为是目标
    }, false);
    inner.addEventListener('click', function(event){
        console.log('inner clicked! true 2'); // 第3执行,因为是目标
    }, true);
    inner.addEventListener('click', function(event){
        console.log('inner clicked! false 3'); // 第4执行,因为是目标
    }, false);
    
    window.addEventListener('click', function(event){
        console.log('window clicked! false');
    }, false);
    window.addEventListener('click', function(event){
        event.stopPropagation();
        console.log('window clicked! true');
    }, true);
</script>
</body>
</html>

 执行结果如下:


注意:addEventListener在IE8中无效,IE8中只能使用attachEvent,对于attachEvent函数绑定事件函数时,只支持两个参数,这两个参数同addEventListener的前两个参数,第一个是事件名,但事件名比addEventListener来说要多一个前缀on,第二个是事件绑定函数,这个函数带有唯一的参数event。但attachEvent绑定的事件函数因为没有第三个参数,他们都是在冒泡阶段执行,而且默认先后顺序跟addEventListener正好相反,先绑定的后执行,后绑定的先执行。

猜你喜欢

转载自canlynet.iteye.com/blog/2342681
今日推荐