zoukankan      html  css  js  c++  java
  • 事件捕获与事件冒泡

    一、背景

    假设有一个HTML代码片段如下:

    <div id="div">
        <input type="button" value="点击测试"></input>
    </div>

    如果我们同时给 div 元素和 input 元素注册 click 事件,当点击 input 元素时,哪个事件先执行?

    要回答这个问题,先得明白:

    HTML文档是层级嵌套结构,页面元素处理事件时,总是最外层元素最先捕获到事件,再层层向下传递给子元素。这称为事件捕获阶段。

    最里层子元素接收到事件后,再层层向上传递给父元素。这是事件的冒泡阶段。

    两个阶段都可以处理我们感兴趣的事件,这就是下文介绍的事件注册模式。

    二、事件注册模式

    1、内联模式

    <input type="button" value="点击测试" onlcick="alert('click on btn.')"></input>

    这是最古老的事件注册模式,事件处理函数作为 HTML 元素属性被添加,由网景(Netscape)发明,除 IE3 for Mac 外所有浏览器都支持。不推荐使用。

    2、传统模式

    随着 DHTML 的出现,我们处理 web 页面的方式被彻底改变,事件注册模式必须变得灵活多样,以适应这种改变。于是浏览器厂商推出了新的事件注册模式。由于网景最先推出该模式,从而成为事实上的标准,后续包括 IE 在内的所有浏览器都支持该标准。写法如下:

    // 添加事件处理操作
    element.onclick = function(){ alert("click event!"); };

    // 移除事件处理操作
    element.onclick = null;

    3、高级模式

    为了解决传统模式的不足,微软推出了自己的事件注册模式,同时 W3C 也在 DOM2 规范中给出了注册模式。于是就有两种注册模式。

    1)W3C模式

    // 添加一个事件处理函数
    element.addEventListener("click", function(){
        //xxx
    }, false);
    
    // 添加两个事件处理函数
    element.addEventListener("click", doOne, false);
    element.addEventListener("click", doTwo, false);
    
    // 移除事件处理函数
    element.removeEventListener("click", doOne, false);

    W3C 模式接收 3 个参数,分别为事件类型、事件处理函数和事件处理阶段。说明如下:

    • 事件处理阶段参数,true=事件捕获,false=事件冒泡,你不确定的话,直接 false。
    • 事件处理函数中的 this 关键字即为元素自身。
    • 在 DOM3 规范中,新增了eventListenerList 属性,记录当前注册到该元素上的事件处理函数。
    • 即使使用 removeEventListener 方法移除一个未绑定的事件操作,也不会报错。

    2)微软模式

    // 添加一个事件操作
    element.attachEvent("onclick", function(){
        //do something
    });
    
    // 添加两个事件操作
    element.attachEvent("onclick", function(){
        // do something
    });
    element.attachEvent("onclick", handler);
    
    // 移除事件操作
    element.detach("onclick", handler);

    不足:

    》事件只能冒泡,不能捕获;

    》事件处理函数是被引用,而不是复制,所以 this 关键字总是 window,完全没用。

    以上两个不足导致的后果是,当事件冒泡时,我们无法知道当前是哪个元素在处理该事件。

    三、回到问题

    在 W3C 标准未出之前,网景采用“事件捕获”方式处理事件顺序,微软则采用“事件冒泡”方式。这里仅对 W3C 标准做说明。

    W3C 标准兼容两种方式,将事件处理过程分作两个阶段:事件捕获阶段和事件冒泡阶段。如下图所示,注册在事件捕获阶段的事件处理函数在捕获阶段执行,注册在冒泡阶段的处理函数在冒泡阶段执行。

                           / 
    ----------| 捕 |------| 冒 |----------- | div | 获 | | 泡 | | | --------| 阶 |------| 阶 |-----------| | input | 段 | | 段 | |
    | / | |--------------------------------------

    当采用传统模式注册事件处理函数时,实际使用的是事件冒泡处理方式。

    所以如果采用传统模式注册事件,则点击input元素时,先执行绑定在input上的click事件,再执行绑定在div上的click事件;

    如果使用W3C标准,则根据第三个参数决定:true:捕获阶段处理,先执行div上的事件,再执行input上的事件;false则顺序相反。

    在低版本 IE 浏览器下,只支持事件冒泡模式。

    三、阻止事件传播

    在使用传统模式和W3C标准时,事件处理函数默认接受一个事件对象参数。

    如果不想让注册在父元素上的事件被子元素捕获,或者子元素的事件不想冒泡到父元素,则可以调用事件对象的 stopPropagation 方法。

    比如 input 是 div 的子元素,所以当点击 input 时,会同时触发注册在 div 和 input 上的事件。

    如果只想触发注册在 div 上的元素,则应在捕获阶段处理 div 上的事件,并在 div 的事件处理函数中调用 event 的 stopPropagation 方法,阻止事件向子元素传播。

    如果希望点击 input 时只触发 input 的 click 事件,则应在冒泡阶段处理 div 上的事件,并在 input 的事件处理函数中调用 event 的 stopPropagation 方法,阻止事件冒泡到父元素。

    参考链接:Early event handlersTraditional event registration modelAdvanced event registration modelsEvent order

  • 相关阅读:
    Python学习总结之三 -- 优雅的字符串
    Python学习总结之二 -- 数据类型
    Python学习总结之一 -- 基础篇
    JavaWeb学习总结第三篇--走进JSP页面元素
    JavaWeb学习总结第二篇--第一个JavaWeb程序
    JavaWeb学习总结第一篇--初识JavaWeb
    三俊马 站立会议04
    三俊马 站立会议03
    三俊马 站立会议02
    三俊马 站立会议01
  • 原文地址:https://www.cnblogs.com/goyeah/p/6062999.html
Copyright © 2011-2022 走看看