zoukankan      html  css  js  c++  java
  • 事件传播(事件捕获与事件冒泡)

     EventTarget接口

      DOM 的事件操作(监听和触发),都定义在EventTarget接口。所有节点对象都部署了这个接口,其他一些需要事件通信的浏览器内置对象(比如,XMLHttpRequestAudioNodeAudioContext)也部署了这个接口。

    该接口主要提供三个实例方法。

    • addEventListener:绑定事件的监听函数
    • removeEventListener:移除事件的监听函数
    • dispatchEvent:触发事件

    事件冒泡和事件捕获分别由微软和网景公司提出,这两个概念都是为了解决页面中事件流(事件发生顺序)的问题。

    事件冒泡

    微软提出了名为事件冒泡(event bubbling)的事件流。事件冒泡可以形象地比喻为把一颗石头投入水中,泡泡会一直从水底冒出水面。也就是说,事件会从最内层的元素开始发生,一直向上传播,直到document对象。

    因此在事件冒泡的概念下在p元素上发生click事件的顺序应该是

    p -> div -> body -> html -> document

    事件捕获

    网景提出另一种事件流名为事件捕获(event capturing)。与事件冒泡相反,事件会从最外层开始发生,直到最具体的元素。

    因此在事件捕获的概念下在p元素上发生click事件的顺序应该是

    document -> html -> body -> div -> p

    在微软和网景打得火热之时,w3c 采用折中的方式,制定了统一的标准——先捕获再冒泡

    addEventListener的第三个参数就是为冒泡和捕获准备的.

    addEventListener有三个参数:

    element.addEventListener(event, function, useCapture)

    第一个参数是需要绑定的事件
    第二个参数是触发事件后要执行的函数
    第三个参数默认值是false,表示在事件冒泡阶段调用事件处理函数;如果参数为true,则表示在事件捕获阶段调用处理函数。

    注意,第三个参数useCapture除了布尔值,还可以是一个属性配置对象。该对象有以下属性。

    • capture:布尔值,表示该事件是否在捕获阶段触发监听函数。
    • once:布尔值,表示监听函数是否只触发一次,然后就自动移除。
    • passive:布尔值,表示监听函数不会调用事件的preventDefault方法。如果监听函数调用了,浏览器将忽略这个要求,并在监控台输出一行警告。

    事件捕获vs事件冒泡

    当事件捕获和事件冒泡一起存在的情况,事件又是如何触发呢。
    这里把被点击的DOM节点为target节点

    1. document 往 target节点,捕获前进,遇到注册的捕获事件立即触发执行

    2. 到达target节点,触发事件(对于target节点上,是先捕获还是先冒泡则按照捕获事件和冒泡事件的注册顺序,先注册先执行)

    3. target节点 往 document 方向,冒泡前进,遇到注册的冒泡事件立即触发

    总结下就是:

      • 对于非target节点则先执行捕获在执行冒泡

      • 对于target节点则是先执行先注册的事件,无论冒泡还是捕获

    冒泡还是捕获?

    对于事件代理来说,在事件捕获或者事件冒泡阶段处理并没有明显的优劣之分,但是由于事件冒泡的事件流模型被所有主流的浏览器兼容,从兼容性角度来说还是建议大家使用事件冒泡模型。

    IE浏览器兼容

    IE浏览器对addEventListener兼容性并不算太好,只有IE9以上可以使用。

    要兼容旧版本的IE浏览器,可以使用IE的attachEvent函数

    object.attachEvent(event, function)

    两个参数与addEventListener相似,分别是事件和处理函数,默认是事件冒泡阶段调用处理函数,要注意的是,写事件名时候要加上"on"前缀("onload"、"onclick"等)。

    封装函数

     1        //添加事件
     2             function addEvent(obj,type,fn){
     3                 if(obj.addEventListener){
     4                     obj.addEventListener(type,fn,false);
     5                 }else{
     6                     obj.attachEvent("on"+type,fn);
     7                 }
     8             }
     9             //移除事件
    10             function romeveEvent(obj,type,fn){
    11                 if(obj.removeEventListener){
    12                     obj.removeEventListener(type,fn)
    13                 }else{
    14                     obj.detachEvent("on"+type,fn);
    15                 }
    16             }

    事件冒泡应用:事件代理(事件委托)

      由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。这种方法叫做事件的代理(delegation)。

    在实际的开发当中,利用事件流的特性,我们可以使用一种叫做事件代理(事件委托)的方法。

      使用事件代理的好处不仅在于将多个事件处理函数减为一个,而且对于不同的元素可以有不同的处理方法。

    var oUl=document.querySelector("ul");
    oUl.onclick=function(e){
      var evt = e || event;
      if(evt.nodeName.toLowerCase=="li"){
        alert("li");  
      }       
    }

     接口target属性是对Event调度事件的对象的引用。它与Event.currentTarget在事件的冒泡或捕获阶段调用事件处理程序时不同

    event.target可以使用属性来实现事件代理

    在IE 6-8上,事件模型是不同的。事件侦听器使用非标准EventTarget.attachEvent()方法附加在此模型中,事件对象具有Event.srcElement属性而不是target属性,并且它具有与之相同的语义Event.target

    function hide(e) {
      // 兼容处理
      var evt=e || event;
      var target = evt.target || evt.srcElement;
      target.style.visibility = 'hidden';
    }

    如果希望事件到某个节点为止,不再传播,可以使用事件对象的stopPropagation方法(IE为cancelBubble)。

    if(evt.stopPropagation){
        evt.stopPropagation();
    }else{
        evt.cancelBubble = true;
    }        

    但是,stopPropagation方法只会阻止事件的传播,不会阻止该事件触发<p>节点的其他click事件的监听函数。也就是说,不是彻底取消click事件。

    p.addEventListener('click', function (event) {
      event.stopPropagation();
     //会触发 console.log(
    1); }); p.addEventListener('click', function(event) { // 会触发 console.log(2); });

    上面代码中,p元素绑定了两个click事件的监听函数。stopPropagation方法只能阻止这个事件向其他元素传播。因此,第二个监听函数会触发。输出结果会先是1,然后是2。

    如果想要彻底阻止这个事件的传播,不再触发后面所有click的监听函数,可以使用stopImmediatePropagation方法。

    p.addEventListener('click', function (event) {
      event.stopImmediatePropagation();
      // 会触发
      console.log(1);
    });
    
    p.addEventListener('click', function(event) {
      // 不会被触发
      console.log(2);
    });

    上面代码中,stopImmediatePropagation方法可以彻底阻止这个事件传播,使得后面绑定的所有click监听函数都不再触发。所以,只会输出1,不会输出2。

    阮一峰老师网站上有着详尽的解释,如有需求,敬请关注

  • 相关阅读:
    RabbitMQ的应用场景以及基本原理介绍
    spring 读取配置文件,将值注入到静态字段
    单元测试不依赖于容器,任何依赖容器的测试在单元测试中都行不通(切记,切记)
    在 KVM 上安装 Win7 虚拟机
    识别 Linux上的设备(磁盘)类型
    云与备份之(1):VMware虚机备份和恢复
    理解 OpenStack + Ceph (9): Ceph 的size/min_size/choose/chooseleaf/scrubbing/repair 等概念
    OpenStack 企业私有云的若干需求(10):OpenStack 的前景和钱景
    OpenStack 企业私有云的若干需求(9): 云管理平台 CMP
    理解 OpenStack + Ceph (8): 基本的 Ceph 性能测试工具和方法
  • 原文地址:https://www.cnblogs.com/gitByLegend/p/10492553.html
Copyright © 2011-2022 走看看