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

  • 相关阅读:
    oracle中Blob和Clob类型的区别
    为什么要分库分表
    Enable file editing in Visual Studio's debug mode
    SQL Server Dead Lock Log
    Debug .NET Framework Source
    SQL Server text field里面有换行符的时候copy到excel数据会散乱
    诊断和修复Web测试记录器(Web Test Recorder)问题
    Can't load Microsoft.ReportViewer.ProcessingObjectModel.dll
    'telnet' is not recognized as an internal or external command
    Linq to XML
  • 原文地址:https://www.cnblogs.com/goyeah/p/6062999.html
Copyright © 2011-2022 走看看