浏览器事件机制与自定义事件的实现

 

一、 0 级 DOM 上的事件和 2 级 DOM 事件机制

0 级 DOM 上的事件又称原始事件模型,所有的浏览器都支持他,而且是通用的。 2 级 DOM 事件机制又为标准事件模型,除了 ie 其他浏览器都支持( ie9 据说也支持,有待考证), ie 虽然大部分与标准事件模型一样,但有自己专有的事件模型,因此开发人员要实现标准事件模型必须为 IE 写特定的代码,这给程序员增加了负担。

原始事件模型

 

Html代码
  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
  2. <html>  
  3.   <head>  
  4.     <title>浏览器0级DOM上的事件</title>  
  5.     <meta http-equiv="content-type" content="text/html; charset=UTF-8">  
  6.   </head>  
  7.      
  8.   <body>  
  9.     <input type="button" value = "点击我" id = "btn">  
  10.   </body>  
  11. </html>  
  12.   
  13. <script type="text/javascript">  
  14. <!--   
  15. var method1 = function(){alert(1)};   
  16. var method2 = function(){alert(2)};   
  17. var method3 = function(){alert(3)};   
  18. document.getElementById("btn").onclick = method1;    
  19. document.getElementById("btn").onclick = method2;    
  20. document.getElementById("btn").onclick = method3;   
  21. //-->  
  22. </script>  

      以上书写在各浏览器中都是兼容的,但只有 medhot3 被执行,即同一个对象同一类型的事件只能注册一个处理函数,要想实现注册多个处理函数,需要利用 2 级 DOM 事件机制。

 

Html代码
  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
  2. <html>  
  3.   <head>  
  4.     <title>浏览器2级DOM事件机制</title>  
  5.     <meta http-equiv="content-type" content="text/html; charset=UTF-8">  
  6.   </head>  
  7.      
  8.   <body>  
  9.     <input type="button" value = "点击我" id = "btn">  
  10.   </body>  
  11. </html>  
  12.   
  13. <script type="text/javascript">  
  14. <!--   
  15. var method1 = function(){alert(1)};   
  16. var method2 = function(){alert(2)};   
  17. var method3 = function(){alert(3)};   
  18.   
  19. //执行顺序为method1->method2->method3    
  20. //标准事件模型   
  21. var btn1Obj = document.getElementById("btn");    
  22. btn1Obj.addEventListener("click",method1,false);    
  23. btn1Obj.addEventListener("click",method2,false);    
  24. btn1Obj.addEventListener("click",method3,false);   
  25.   
  26. //执行顺序为method3->method2->method1   
  27. //IE事件模型   
  28. var btn1Obj = document.getElementById("btn");   
  29. btn1Obj.attachEvent("onclick",method1);    
  30. btn1Obj.attachEvent("onclick",method2);    
  31. btn1Obj.attachEvent("onclick",method3);   
  32.   
  33. //-->  
  34. </script>  

 

     从运行结果来看, ie 和 firefox 下执行的顺序是不一样的

 

二、2级DOM事件模型事件的注册与删除

element.addEventListener(eventType,fn,useCapture); // 注册事件

element.removeEventListener(eventType,fn, useCapture);// 删除事件

 

      可以用 addEventListener() 给同一个对象同一类型的事件注册多个处理函数,但是如果在同一元素上多次注册了一个处理函数,那么第一次注册后的所有注册都将被忽略,但删除该注册函数(调用 removeEventListener() )后可以再重新注册该函数。需要注意的是删除事件, useCapture 的值必须要跟注册时保持一致

 

 

Html代码
  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"   
  2.      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
  3. <html xmlns="http://www.w3.org/1999/xhtml" lang="zh-CN">  
  4. <head>  
  5. <meta http-equiv="Content-Type" content="text/html; charset=utf-8"    />  
  6. <title>浏览器事件机制——注册和删除事件</title>  
  7. <style>  
  8.      div {border:1px  solid blue;}   
  9.      div#div1 {padding:40px;background-color:#aaaaaa;}   
  10.      div#div2 {padding:40px;background-color:#bbbbbb;}   
  11.      div#div3 {padding:40px;background-color:#cccccc;}   
  12. </style>  
  13. </head>  
  14.  <body>  
  15. <div id="div1" style="width:100px;height:100px;" >  
  16.       我是老大, 点击我添加老三的click事件   
  17. </div>  
  18. <br/>  
  19. <div id="div2" style="width:100px;height:110px;" >  
  20.     我是老二, 点击我删除老三的click事件           
  21. </div>  
  22. <br/>  
  23. <div id="div3" style="width:100px;height:100px;" >  
  24.     我是老三,是否有click事件,老大老二说了算,呵呵   
  25. </div>  
  26. <script>  
  27.     function click1() {   
  28.         alert("I am div1,add div3 event");   
  29.         if(window.addEventListener){   
  30.             div3.addEventListener("click", click3, false);   
  31.         }else if (window.attachEvent){   
  32.             div3.attachEvent("onclick", click3);   
  33.         }   
  34.     }   
  35.     function click2() {   
  36.         alert("I am div2,remove div3 event");   
  37.         if(window.addEventListener){   
  38.             div3.removeEventListener("click", click3, false);   
  39.         }else if (window.attachEvent){   
  40.             div3.detachEvent("onclick", click3);   
  41.         }   
  42.     }   
  43.     function click3() {   
  44.         alert("I am div3");   
  45.     }   
  46.        
  47.     var div1 = document.getElementById("div1");   
  48.     var div2 = document.getElementById("div2");   
  49.     var div3 = document.getElementById("div3");   
  50.        
  51.     if(window.addEventListener){   
  52.         div1.addEventListener("click", click1, false);   
  53.         div2.addEventListener("click", click2, false);   
  54.     }else if (window.attachEvent){   
  55.         div1.attachEvent("onclick", click1);   
  56.         div2.attachEvent("onclick", click2);   
  57.     }   
  58. </script>       
  59. </body>  
  60. </html>  

 

 

三、2级DOM事件冒泡模型(Bubble Model)

 

      在2级DOM事件模型中,事件传播分三个阶段进行,即捕获阶段(capturing)、目标阶段和冒泡阶段(bubbling)。在捕获阶段,事件从Document对象沿着文档树向下传播给目标节点,如果目标的任何一个祖先(不是目标本身)专门注册了捕获事件句柄,那么在事件传播过程中,就会运行这些句柄,在冒泡阶段,事件将从目标元素向上传播回或气泡回Document对象的文档层次。虽然所有事件都受事件传播的捕获阶段的支配,但并非所有类型的事件都起泡。

      在注册事件时,useCapture参数确定侦听器是运行于捕获阶段、目标阶段还是冒泡阶段。 如果将 useCapture 设置为 true,则侦听器只在捕获阶段处理事件,而不在目标或冒泡阶段 处理事件。 如果useCapture 为 false,则侦听器只在目标或冒泡阶段处理事件。 要在所有三个阶段都侦听事件,需调用两次 addEventListener,一次将 useCapture 设置为 true,第二次再将useCapture 设置为 false。

 

Html代码
  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"   
  2.      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
  3. <html xmlns="http://www.w3.org/1999/xhtml" lang="zh-CN">  
  4. <head>  
  5. <meta http-equiv="Content-Type" content="text/html; charset=utf-8"    />  
  6. <title>浏览器事件机制——冒泡处理</title>  
  7. <style>  
  8.      div {border:1px  solid blue;}   
  9.      div#divGrandpa {padding:40px;background-color:#aaaaaa;}   
  10.      div#divFather {padding:40px;background-color:#bbbbbb;}   
  11.      div#divSon {padding:40px;background-color:#cccccc;}   
  12. </style>  
  13. </head>  
  14.  <body>  
  15. <div id="divGrandpa" style="width:300px;height:200px;" >  
  16.        <div id="divFather" style="width:200px;height:120px;" >  
  17.             <div id="divSon" style="width:100px;height:40px;" >  
  18.                 点击我   
  19.             </div>  
  20.        </div>  
  21. </div>  
  22. <script>  
  23.     function showSon() {   
  24.         alert("I am son");   
  25.     }   
  26.     function showFather() {   
  27.         alert("I am father");   
  28.     }   
  29.     function showGrandpa() {   
  30.         alert("I am Grandpa");   
  31.     }   
  32.     var grandpa = document.getElementById("divGrandpa");   
  33.     var father = document.getElementById("divFather");   
  34.     var son = document.getElementById("divSon");   
  35.     if(window.addEventListener){   
  36.         grandpa.addEventListener("click", showGrandpa, false);   
  37.         father.addEventListener("click", showFather, false);   
  38.         son.addEventListener("click", showSon, false);   
  39.     }else if (window.attachEvent){   
  40.         grandpa.attachEvent("onclick", showGrandpa);   
  41.         father.attachEvent("onclick", showFather);   
  42.         son.attachEvent("onclick", showSon);   
  43.     }   
  44. </script>       
  45. </body>  
  46. </html>  

 

     从运行结果来看,对于ie,在ie(ie8之前的版本,包括ie8)中当点击son节点时,会分别弹出I am son、I am father和I am Grandpa,即事件最先被底层的结点触发,再逐渐上传,直到最外层的结点,冒泡方式为儿子——>父亲的模式;在Firefox等支持标准事件模型的浏览器中,跟addEventListener的Capture参数有关,当设置为true时,为捕获模式,事件会从最顶层的结点往下传输,即 父亲——>儿子的传播模式。当设为false(默认值)时,则会按冒泡模式传递事件。另外由于ie9即支持window.attachEvent,又支持window.addEventListener,所以会根据代码的书写来运行其效果的。


四、如何停止事件的传递


在IE浏览器中可以调用以下代码
event.cancelBubble = true;
在Firefox等遵循W3C规范的浏览器中,可以调用以下代码
e.stopPropagation();

      调用以上代码后可以终止事件在传播过程的捕获、目标处理或起泡阶段进一步传播。调用该方法后,该节点上处理该事件的处理程序将被调用,事件不再被分派到其他节点(即不再进一步传播)。
      该方法(属性)将停止事件的传播,阻止它被分派到其他 Document 节点。在事件传播的任何阶段都可以调用它。注意,虽然该方法不能阻止同一个 Document 节点上的其他事件句柄被调用,但是它可以阻止把事件分派到其他节点。

 

Html代码
  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"   
  2.      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
  3. <html xmlns="http://www.w3.org/1999/xhtml" lang="zh-CN">  
  4. <head>  
  5. <meta http-equiv="Content-Type" content="text/html; charset=utf-8"    />  
  6. <title>浏览器事件机制——停止事件的进一步传递</title>  
  7. <style>  
  8.      div {border:1px  solid blue;}   
  9.      div#divGrandpa {padding:40px;background-color:#aaaaaa;}   
  10.      div#divFather {padding:40px;background-color:#bbbbbb;}   
  11.      div#divSon {padding:40px;background-color:#cccccc;}   
  12. </style>  
  13. </head>  
  14.  <body>  
  15. <div id="divGrandpa" style="width:300px;height:200px;" >  
  16.        <div id="divFather" style="width:200px;height:120px;" >  
  17.             <div id="divSon" style="width:100px;height:40px;" >  
  18.                 点击我   
  19.             </div>  
  20.        </div>  
  21. </div>  
  22.   
  23. <script>  
  24.     function showSon(e) {   
  25.         alert("I am son");   
  26.     }   
  27.     function showFather(e) {   
  28.         //IE把event对象作为window对象的一个属性,而W3C把event对象作为处理程序的一个参数   
  29.         ee = e || event;   
  30.         if(e.stopPropagation){   
  31.             e.stopPropagation();   
  32.         }else{   
  33.             e.cancelBubble = true;   
  34.         }   
  35.         alert("I am father");   
  36.     }   
  37.     function showGrandpa(e) {   
  38.         alert("I am Grandpa");   
  39.     }   
  40.     var grandpa = document.getElementById("divGrandpa");   
  41.     var father = document.getElementById("divFather");   
  42.     var son = document.getElementById("divSon");   
  43.     if(window.addEventListener){   
  44.         grandpa.addEventListener("click", showGrandpa, false);   
  45.         father.addEventListener("click", showFather, false);   
  46.         son.addEventListener("click", showSon, false);   
  47.     }else if (window.attachEvent){   
  48.         grandpa.attachEvent("onclick", showGrandpa);   
  49.         father.attachEvent("onclick", showFather);   
  50.         son.attachEvent("onclick", showSon);   
  51.     }   
  52. </script>       
  53. </body>  
  54. </html>  

五、自定义事件

 

1、不带参数事件处理,也是最简单的事件设计模式


      最简单的一种模式是将一个类的方法成员定义为事件,通常是一个空函数,当程序需要处理该事件时,再进行扩充该事件接口。比如:

 

Js代码
  1. function Class1(){   
  2.         //构造函数   
  3.     }   
  4.     Class1.prototype = {   
  5.         show : function(){   
  6.             this.onShow();//触发onShow事件   
  7.         },   
  8.         onShow : function(){}//定义事件接口   
  9.     }   
  10.     //创建class1实例   
  11.     var obj = new Class1();   
  12.     //创建obj的onShow事件处理程序   
  13.     obj.onShow = function(){   
  14.         alert('onshow event');   
  15.     }   
  16.     //调用obj的show方法   
  17.     obj.show();  

    以上实现,每个事件接口仅能绑定1个事件处理程序


2、给事件处理程序传递参数

Js代码
  1. //将有参数的函数封装为无参数的函数   
  2.     function createFunction(obj, strFn){   
  3.         obj = obj || window;   
  4.         var args = [];   
  5.         for(var i = 2; i < arguments.length; i++){   
  6.             args.push(arguments[i]);   
  7.         }   
  8.         return function(){   
  9.             //该语句相当于obj[strFn](args[0],args[1],...);   
  10.             obj[strFn].apply(obj,args);   
  11.         }   
  12.     }   
  13.     //定义类 Class1   
  14.     function Class1(){   
  15.         //构造函数   
  16.     }   
  17.     Class1.prototype = {   
  18.         show : function(){   
  19.             this.onShow();//触发onShow事件   
  20.         },   
  21.         onShow : function(){}//定义事件接口   
  22.     }   
  23.     //创建class1实例   
  24.     var obj = new Class1();   
  25.     //创建obj的onShow事件处理程序   
  26.     function objOnShow(userName){   
  27.         alert('hello, ' + userName);   
  28.     }   
  29.     var userName = 'xiaowang';   
  30.     //绑定obj的onShow事件   
  31.     obj.onShow = createFunction(null,'objOnShow',userName);   
  32.     //调用obj的show方法   
  33.     obj.show();  

      在以上代码中,将变量userName作为参数传递给了objOnShow事件处理程序。事实上,obj.onShow 得到的事件处理程序并不是objOnShow,而是由createFunction返回的一个无参函数


3、自定义事件支持多绑定

Js代码
  1. //定义类 Class1   
  2.     function Class1(){   
  3.         //构造函数   
  4.     }   
  5.     Class1.prototype = {   
  6.         show : function(){   
  7.             //如果有事件绑定则循环onshow数组,触发该事件   
  8.             if(this.onshow){   
  9.                 for(var i = 0, len = this.onshow.length; i < len; i++){   
  10.                     this.onshow[i]();//调用事件处理程序   
  11.                 }    
  12.             }   
  13.         },   
  14.         addEventOnShow : function (_eHandler){   
  15.             this.onshow = this.onshow || [];//用数组存储绑定的事件处理程序引用   
  16.             this.onshow.push(_eHandler);   
  17.         }   
  18.     }   
  19.     //创建class1实例   
  20.     var obj = new Class1();   
  21.     //事件一   
  22.     function onShow1(){   
  23.         alert('event1');   
  24.     }   
  25.     //事件二   
  26.     function onShow2(){   
  27.         alert('event2');   
  28.     }   
  29.     //绑定事件   
  30.     obj.addEventOnShow(onShow1);   
  31.     obj.addEventOnShow(onShow2);   
  32.     //调用obj的show方法   
  33.     obj.show();  

4、自定义事件支持带参数的多绑定

Js代码
  1. //将有参数的函数封装为无参数的函数   
  2.     function createFunction(obj, strFn){   
  3.         obj = obj || window;   
  4.         var args = [];   
  5.         for(var i = 2; i < arguments.length; i++){   
  6.             args.push(arguments[i]);   
  7.         }   
  8.         return function(){   
  9.             //该语句相当于obj[strFn](args[0],args[1],...);   
  10.             obj[strFn].apply(obj,args);   
  11.         }   
  12.     }   
  13.     //定义类 Class1   
  14.     function Class1(){   
  15.         //构造函数   
  16.     }   
  17.     Class1.prototype = {   
  18.         show : function(){   
  19.             //如果有事件绑定则循环onshow数组,触发该事件   
  20.             if(this.onshow){   
  21.                 for(var i = 0, len = this.onshow.length; i < len; i++){   
  22.                     this.onshow[i]();//调用事件处理程序   
  23.                 }    
  24.             }   
  25.         },   
  26.         addEventOnShow : function (_eHandler){   
  27.             this.onshow = this.onshow || [];//用数组存储绑定的事件处理程序引用   
  28.             this.onshow.push(_eHandler);   
  29.         }   
  30.     }   
  31.     //创建class1实例   
  32.     var obj = new Class1();   
  33.     //创建obj的onShow事件处理程序   
  34.     function objOnShow(userName){   
  35.         alert('hello, ' + userName);   
  36.     }   
  37.     //事件一   
  38.     var  userName1 = 'xiaowang';   
  39.     var  onShow1 = createFunction(null,'objOnShow',userName1);   
  40.     //事件一   
  41.     var  userName2 = 'xiaoli';   
  42.     var  onShow2 = createFunction(null,'objOnShow',userName2);   
  43.     //绑定事件   
  44.     obj.addEventOnShow(onShow1);   
  45.     obj.addEventOnShow(onShow2);   
  46.     //调用obj的show方法   
  47.     obj.show();  

   以上实现把带参数和多绑定结合在一起,还可以增加一个removeEventOnShow来删除已注册的事件。

 

六、把对象注册为事件句柄

 

      在编写面向对象的JavaScript程序时,如果想用对象作为事件句柄,那么可以使用如下的函数来注册它们:

function registerObjectEventHandler(element,eventtype,listener,captures){
    element.addEventListener(eventtype,
        function(event) {listener.handleEvent(event);},captures);
}

      用这个函数可以把任何对象注册为事件句柄,只要它定义了handleEvent()方法。Firefox(以及其他基于Mozilla代码的浏览器)允许直接把定义了handleEvent()方法的事件监听器对象传递给addEventListener()方法而不是函数引用。对于这些浏览器来说,不需要我们刚才给出的特殊注册函数。

 

请看下面的例子

 

Html代码
  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
  2. <html>  
  3.   <head>  
  4.     <title>把对象注册为事件句柄</title>  
  5.     <meta http-equiv="content-type" content="text/html; charset=UTF-8">  
  6.   </head>  
  7.      
  8.   <body>  
  9.     <input type="button" value = "点击我" id = "btn">  
  10.   </body>  
  11. </html>  
  12.   
  13. <script type="text/javascript">  
  14. <!--   
  15.     var EventHandler = function(){}   
  16.     EventHandler.prototype.handleEvent = function(event){   
  17.         alert('用对象作为事件句柄,只要实现该对象的方法handleEvent即可');   
  18.         alert(event.type);   
  19.     }   
  20.     var objectHandler = new EventHandler();   
  21.     var btn1Obj = document.getElementById("btn");    
  22.     if(window.addEventListener){   
  23.         btn1Obj.addEventListener("click",objectHandler,false);    
  24.     }else if (window.attachEvent){   
  25.         //btn1Obj.attachEvent("onclick",objectHandler);//调用失败,说明不支持把对象注册为事件句柄   
  26.         //btn1Obj.attachEvent("onclick",objectHandler.handleEvent);   
  27.         registerObjectEventHandler(btn1Obj,"onclick",objectHandler);   
  28.     }   
  29.   
  30.     /**   
  31.      * 对于不支持把对象注册为事件句柄的浏览器,可以调用以下方法来实现   
  32.      */   
  33.     function registerObjectEventHandler(element,eventtype,listener,captures){   
  34.         if(window.addEventListener){   
  35.             element.addEventListener(eventtype,   
  36.                 function(event) {listener.handleEvent(event);},captures);   
  37.         }else if (window.attachEvent){   
  38.             element.attachEvent(eventtype,   
  39.                 function(event) {listener.handleEvent(event);});   
  40.         }   
  41.     }   
  42.   
  43. //-->  
  44. </script> 

本文转载自:http://linder0209.iteye.com/blog/961157

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值