JS事件代理

事件阶段
事件分为三个阶段: 事件捕获 –> 事件目标 –> 事件冒泡

事件捕获和冒泡
事件捕获
:事件发生时(onclick,onmouseover……)首先发生在document上,然后依次传递给body、……最后到达目的节点(即事件目标)。

事件冒泡:事件到达事件目标之后不会结束,会逐层向上冒泡,直至document对象,跟事件捕获相反

  • 事件捕获:当某个元素触发某个事件(如onclick),顶层对象document就会发出一个事件流,随着DOM树的节点向目标元素节点流去,直到到达事件真正发生的目标元素。在这个过程中,事件相应的监听函数是不会被触发的。
  • 事件目标:当到达目标元素之后,执行目标元素该事件相应的处理函数。如果没有绑定监听函数,那就不执行。
  • 事件起泡:从目标元素开始,往顶层元素传播。途中如果有节点绑定了相应的事件处理函数,这些函数都会被一次触发。如果想阻止事件起泡,可以使用e.stopPropagation()(Firefox)或者e.cancelBubble=true(IE)来组织事件的冒泡传播。


事件

  1. onlick -->事件冒泡,重写onlick会覆盖之前属性,没有兼容性问题

    el.onclik = null;   //解绑单击事件,将onlick属性设为null即可
  2. addEventListener(event.type, handle, boolean); IE8及以下不支持,属于DOM2级的方法,可添加多个方法不被覆盖

    //事件类型没有on,false 表示在事件第三阶段(冒泡)触发,true表示在事件第一阶段(捕获)触发。 如果handle是同一个方法,只执行一次。
    ele.addEventListener('click', function(){ }, false);  
    //解绑事件,参数和绑定一样
    ele.removeEventListener(event.type, handle, boolean);
  3. attachEvent(event.type, handle ); IE特有,兼容IE8及以下,可添加多个事件处理程序,只支持冒泡阶段

    //如果handle是同一个方法,绑定几次执行几次,这点和addEventListener不同,事件类型要加on,例如onclick而不是click
    ele.attachEvent('onclick', function(){ }); 
    //解绑事件,参数和绑定一样
    ele.detachEvent("onclick", function(){ });
  4. 默认事件行为:href=""链接,submit表单提交等 
    1. return false; 阻止独享属性(通过on这种方式)绑定的事件的默认事件

      ele.onclick = function() {
       ……                         //你的代码
       return false;              //通过返回false值阻止默认事件行为
      };
    2. event.preventDefault( ); 阻止通过 addEventListener( ) 添加的事件的默认事件

      element.addEventListener("click", function(e){
       var event = e || window.event;
       ……
       event.preventDefault( );      //阻止默认事件
      },false);
    3. event.returnValue = false; 阻止通过 attachEvent( ) 添加的事件的默认事件

      element.attachEvent("onclick", function(e){
         var event = e || window.event;
         ……
         event.returnValue = false;       //阻止默认事件
      });

    事件封装

    • 接下来我们把事件绑定以及事件解绑封装成为一个函数,兼容浏览器

      // 事件绑定
      function addEvent(element, eType, handle, bol) {
       if(element.addEventListener){           //如果支持addEventListener
           element.addEventListener(eType, handle, bol);
       }else if(element.attachEvent){          //如果支持attachEvent
           element.attachEvent("on"+eType, handle);
       }else{                                  //否则使用兼容的onclick绑定
           element["on"+eType] = handle;
       }
      }
      // 事件解绑
      function removeEvent(element, eType, handle, bol) {
       if(element.addEventListener){
           element.removeEventListener(eType, handle, bol);
       }else if(element.attachEvent){
           element.detachEvent("on"+eType, handle);
       }else{
           element["on"+eType] = null;
       }
      }

    事件冒泡、事件捕获阻止

    event.stopPropagation( );    // 阻止事件的进一步传播,包括(冒泡,捕获),无参数
    event.cancelBubble = true;   // true 为阻止冒泡

    事件委托(事件代理)

    • 事件委托:利用事件冒泡的特性,将里层的事件委托给外层事件,根据event对象的属性进行事件委托,改善性能。

    • 使用事件委托能够避免对特定的每个节点添加事件监听器;事件监听器是被添加到它们的父元素上。事件监听器会分析从子元素冒泡上来的事件,找到是哪个子元素的事件。

    • 使用事件代理机制,当事件被抛到更上层的父节点的时候,我们通过检查事件的目标对象(target)

    • 来判断并获取事件源。 // 获取父节点,并为它添加一个click事件

    •  document.getElementById("parent-list").addEventListener("click",function(e) 
      

    • { // 检查事件源e.targe是否为Li 
      

    • if(e.target && e.target.nodeName.toUpperCase() == "LI") { 
      

    • // 真正的处理过程在这里 //alert(123); 
      

    • console.log("List item ",e.target.id," was clicked!");
        }
      });


    来个例子吧,如果要单独点击table里面的td,普通做法是for循环给每个td绑定事件,td少的话性能什么差别,td如果多了,就不行了,我们使用事件委托:

     <!-- HTML -->
    <table id="out" border="1" style="cursor: pointer;">
        <tr>
          <td>table01</td>
          <td>table02</td>
          <td>table03</td>
          <td>table04</td>
          <td>table05</td>
          <td>table06</td>
          <td>table07</td>
          <td>table08</td>
          <td>table09</td>
          <td>table10</td>
        </tr>
    </table>
    
      // JS
      var out = document.getElementById("out");
      if(out.addEventListener){
          out.addEventListener("click",function(e){
              var e = e||window.event;
              //IE没有e.target,有e.srcElement
              var target = e.target||e.srcElement;
              //判断事件目标是否是td,是的话target即为目标节点td
              if(target.tagName.toLowerCase()=="td"){
                  changeStyle(target);
                  console.log(target.innerHTML);
              }
          },false);
      }else{
          out.attachEvent("onclick",function(e){
              var e = e||window.event;
              //IE没有e.target,有e.srcElement
              var target = e.target||e.srcElement;
              //判断事件目标是否是td,是的话target即为目标节点td
              if(target.tagName.toLowerCase()=="td"){
                  changeStyle(target);
                  console.log(target.innerHTML);
              }
          });
      };
    };
    

    1)冒泡型事件:事件按照从最特定的事件目标到最不特定的事件目标(document对象)的顺序触发。

      IE 5.5: div -> body -> document

      IE 6.0: div -> body -> html -> document

      Mozilla 1.0: div -> body -> html -> document -> window

    (2)捕获型事件(event capturing):事件从最不精确的对象(document 对象)开始触发,然后到最精确(也可以在窗口级别捕获事件,不过必须由开发人员特别指定)。

    (3)DOM事件流:同时支持两种事件模型:捕获型事件和冒泡型事件,但是,捕获型事件先发生。两种事件流会触及DOM中的所有对象,从document对象开始,也在document对象结束。

      DOM事件模型最独特的性质是,文本节点也触发事件(在IE中不会)。

    支持W3C标准的浏览器在添加事件时用addEventListener(event,fn,useCapture)方法,基中第3个参数useCapture是一个Boolean值,用来设置事件是在事件捕获时执行,还是事件冒泡时执行。而不兼容W3C的浏览器(IE)用attachEvent()方法,此方法没有相关设置,不过IE的事件模型默认是在事件冒泡时执行的,也就是在useCapture等于false的时候执行,所以把在处理事件时把useCapture设置为false是比较安全,也实现兼容浏览器的效果。

事件捕获阶段:事件从最上一级标签开始往下查找,直到捕获到事件目标(target)。
事件冒泡阶段:事件从事件目标(target)开始,往上冒泡直到页面的最上一级标签。

假设一个元素div,它有一个下级元素p。
<div>
  <p>元素</p>
</div>
这两个元素都绑定了click事件,如果用户点击了p,它在div和p上都触发了click事件,那这两个事件处理程序哪个先执行呢?事件顺序是什么?
 
两种模型
以前,Netscape和Microsoft是不同的实现方式。

Netscape中,div先触发,这就叫做事件捕获。

Microsoft中,p先触发,这就叫做事件冒泡。

两种事件处理顺序刚好相反。IE只支持事件冒泡,Mozilla, Opera 7 和 Konqueror两种都支持,旧版本的Opera's 和 iCab两种都不支持 。

事件捕获
当你使用事件捕获时,父级元素先触发,子级元素后触发,即div先触发,p后触发。

事件冒泡
当你使用事件冒泡时,子级元素先触发,父级元素后触发,即p先触发,div后触发。

W3C模型
W3C模型是将两者进行中和,在W3C模型中,任何事件发生时,先从顶层开始进行事件捕获,直到事件触发到达了事件源元素。然后,再从事件源往上进行事件冒泡,直到到达document。

程序员可以自己选择绑定事件时采用事件捕获还是事件冒泡,方法就是绑定事件时通过addEventListener函数,它有三个参数,第三个参数若是true,则表示采用事件捕获,若是false,则表示采用事件冒泡。

ele.addEventListener('click',doSomething2,true)

true=捕获

false=冒泡

传统绑定事件方式
在一个支持W3C DOM的浏览器中,像这样一般的绑定事件方式,是采用的事件冒泡方式。

ele.onclick = doSomething2

IE浏览器
如上面所说,IE只支持事件冒泡,不支持事件捕获,它也不支持addEventListener函数,不会用第三个参数来表示是冒泡还是捕获,它提供了另一个函数attachEvent。

ele.attachEvent("onclick", doSomething2);

附:事件冒泡(的过程):事件从发生的目标(event.srcElement||event.target)开始,沿着文档逐层向上冒泡,到document为止。

事件的传播是可以阻止的:
• 在W3c中,使用stopPropagation()方法
• 在IE下设置cancelBubble = true;
在捕获的过程中stopPropagation();后,后面的冒泡过程也不会发生了~
3.阻止事件的默认行为,例如click <a>后的跳转~
• 在W3c中,使用preventDefault()方法;
• 在IE下设置window.event.returnValue = false;
4.哇,终于写完了,一边测试一边写的额,不是所有的事件都能冒泡,例如:blur、focus、load、unload,(这个是从别人的文章里摘过来的,我没测试)。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值