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

本文详细介绍了DOM事件的不同级别机制,包括0级DOM上的事件和2级DOM事件机制的区别,2级DOM事件模型的注册与删除,事件冒泡模型,如何停止事件的传递,以及自定义事件的实现方法。

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

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

原始事件模型

Html代码
  1. <!DOCTYPEHTMLPUBLIC"-//W3C//DTDHTML4.01Transitional//EN">
  2. <html>
  3. <head>
  4. <title>浏览器0级DOM上的事件</title>
  5. <metahttp-equiv="content-type"content="text/html;charset=UTF-8">
  6. </head>
  7. <body>
  8. <inputtype="button"value="点击我"id="btn">
  9. </body>
  10. </html>
  11. <scripttype="text/javascript">
  12. <!--
  13. varmethod1=function(){alert(1)};
  14. varmethod2=function(){alert(2)};
  15. varmethod3=function(){alert(3)};
  16. document.getElementById("btn").onclick=method1;
  17. document.getElementById("btn").onclick=method2;
  18. document.getElementById("btn").onclick=method3;
  19. //-->
  20. </script>

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

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

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

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

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

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

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

Html代码
  1. <!DOCTYPEhtmlPUBLIC"-//W3C//DTDXHTML1.0Transitional//EN"
  2. "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  3. <htmlxmlns="http://www.w3.org/1999/xhtml"lang="zh-CN">
  4. <head>
  5. <metahttp-equiv="Content-Type"content="text/html;charset=utf-8"/>
  6. <title>浏览器事件机制——注册和删除事件</title>
  7. <style>
  8. div{border:1pxsolidblue;}
  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. <divid="div1"style="width:100px;height:100px;">
  16. 我是老大,点击我添加老三的click事件
  17. </div>
  18. <br/>
  19. <divid="div2"style="width:100px;height:110px;">
  20. 我是老二,点击我删除老三的click事件
  21. </div>
  22. <br/>
  23. <divid="div3"style="width:100px;height:100px;">
  24. 我是老三,是否有click事件,老大老二说了算,呵呵
  25. </div>
  26. <script>
  27. functionclick1(){
  28. alert("Iamdiv1,adddiv3event");
  29. if(window.addEventListener){
  30. div3.addEventListener("click",click3,false);
  31. }elseif(window.attachEvent){
  32. div3.attachEvent("onclick",click3);
  33. }
  34. }
  35. functionclick2(){
  36. alert("Iamdiv2,removediv3event");
  37. if(window.addEventListener){
  38. div3.removeEventListener("click",click3,false);
  39. }elseif(window.attachEvent){
  40. div3.detachEvent("onclick",click3);
  41. }
  42. }
  43. functionclick3(){
  44. alert("Iamdiv3");
  45. }
  46. vardiv1=document.getElementById("div1");
  47. vardiv2=document.getElementById("div2");
  48. vardiv3=document.getElementById("div3");
  49. if(window.addEventListener){
  50. div1.addEventListener("click",click1,false);
  51. div2.addEventListener("click",click2,false);
  52. }elseif(window.attachEvent){
  53. div1.attachEvent("onclick",click1);
  54. div2.attachEvent("onclick",click2);
  55. }
  56. </script>
  57. </body>
  58. </html>

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

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

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

Html代码
  1. <!DOCTYPEhtmlPUBLIC"-//W3C//DTDXHTML1.0Transitional//EN"
  2. "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  3. <htmlxmlns="http://www.w3.org/1999/xhtml"lang="zh-CN">
  4. <head>
  5. <metahttp-equiv="Content-Type"content="text/html;charset=utf-8"/>
  6. <title>浏览器事件机制——冒泡处理</title>
  7. <style>
  8. div{border:1pxsolidblue;}
  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. <divid="divGrandpa"style="width:300px;height:200px;">
  16. <divid="divFather"style="width:200px;height:120px;">
  17. <divid="divSon"style="width:100px;height:40px;">
  18. 点击我
  19. </div>
  20. </div>
  21. </div>
  22. <script>
  23. functionshowSon(){
  24. alert("Iamson");
  25. }
  26. functionshowFather(){
  27. alert("Iamfather");
  28. }
  29. functionshowGrandpa(){
  30. alert("IamGrandpa");
  31. }
  32. vargrandpa=document.getElementById("divGrandpa");
  33. varfather=document.getElementById("divFather");
  34. varson=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. }elseif(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. <!DOCTYPEhtmlPUBLIC"-//W3C//DTDXHTML1.0Transitional//EN"
  2. "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  3. <htmlxmlns="http://www.w3.org/1999/xhtml"lang="zh-CN">
  4. <head>
  5. <metahttp-equiv="Content-Type"content="text/html;charset=utf-8"/>
  6. <title>浏览器事件机制——停止事件的进一步传递</title>
  7. <style>
  8. div{border:1pxsolidblue;}
  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. <divid="divGrandpa"style="width:300px;height:200px;">
  16. <divid="divFather"style="width:200px;height:120px;">
  17. <divid="divSon"style="width:100px;height:40px;">
  18. 点击我
  19. </div>
  20. </div>
  21. </div>
  22. <script>
  23. functionshowSon(e){
  24. alert("Iamson");
  25. }
  26. functionshowFather(e){
  27. //IE把event对象作为window对象的一个属性,而W3C把event对象作为处理程序的一个参数
  28. ee=e||event;
  29. if(e.stopPropagation){
  30. e.stopPropagation();
  31. }else{
  32. e.cancelBubble=true;
  33. }
  34. alert("Iamfather");
  35. }
  36. functionshowGrandpa(e){
  37. alert("IamGrandpa");
  38. }
  39. vargrandpa=document.getElementById("divGrandpa");
  40. varfather=document.getElementById("divFather");
  41. varson=document.getElementById("divSon");
  42. if(window.addEventListener){
  43. grandpa.addEventListener("click",showGrandpa,false);
  44. father.addEventListener("click",showFather,false);
  45. son.addEventListener("click",showSon,false);
  46. }elseif(window.attachEvent){
  47. grandpa.attachEvent("onclick",showGrandpa);
  48. father.attachEvent("onclick",showFather);
  49. son.attachEvent("onclick",showSon);
  50. }
  51. </script>
  52. </body>
  53. </html>

五、自定义事件

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


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

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

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


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

Js代码
  1. //将有参数的函数封装为无参数的函数
  2. functioncreateFunction(obj,strFn){
  3. obj=obj||window;
  4. varargs=[];
  5. for(vari=2;i<arguments.length;i++){
  6. args.push(arguments[i]);
  7. }
  8. returnfunction(){
  9. //该语句相当于obj[strFn](args[0],args[1],...);
  10. obj[strFn].apply(obj,args);
  11. }
  12. }
  13. //定义类Class1
  14. functionClass1(){
  15. //构造函数
  16. }
  17. Class1.prototype={
  18. show:function(){
  19. this.onShow();//触发onShow事件
  20. },
  21. onShow:function(){}//定义事件接口
  22. }
  23. //创建class1实例
  24. varobj=newClass1();
  25. //创建obj的onShow事件处理程序
  26. functionobjOnShow(userName){
  27. alert('hello,'+userName);
  28. }
  29. varuserName='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. functionClass1(){
  3. //构造函数
  4. }
  5. Class1.prototype={
  6. show:function(){
  7. //如果有事件绑定则循环onshow数组,触发该事件
  8. if(this.onshow){
  9. for(vari=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. varobj=newClass1();
  21. //事件一
  22. functiononShow1(){
  23. alert('event1');
  24. }
  25. //事件二
  26. functiononShow2(){
  27. alert('event2');
  28. }
  29. //绑定事件
  30. obj.addEventOnShow(onShow1);
  31. obj.addEventOnShow(onShow2);
  32. //调用obj的show方法
  33. obj.show();

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

Js代码
  1. //将有参数的函数封装为无参数的函数
  2. functioncreateFunction(obj,strFn){
  3. obj=obj||window;
  4. varargs=[];
  5. for(vari=2;i<arguments.length;i++){
  6. args.push(arguments[i]);
  7. }
  8. returnfunction(){
  9. //该语句相当于obj[strFn](args[0],args[1],...);
  10. obj[strFn].apply(obj,args);
  11. }
  12. }
  13. //定义类Class1
  14. functionClass1(){
  15. //构造函数
  16. }
  17. Class1.prototype={
  18. show:function(){
  19. //如果有事件绑定则循环onshow数组,触发该事件
  20. if(this.onshow){
  21. for(vari=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. varobj=newClass1();
  33. //创建obj的onShow事件处理程序
  34. functionobjOnShow(userName){
  35. alert('hello,'+userName);
  36. }
  37. //事件一
  38. varuserName1='xiaowang';
  39. varonShow1=createFunction(null,'objOnShow',userName1);
  40. //事件一
  41. varuserName2='xiaoli';
  42. varonShow2=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. <!DOCTYPEHTMLPUBLIC"-//W3C//DTDHTML4.01Transitional//EN">
  2. <html>
  3. <head>
  4. <title>把对象注册为事件句柄</title>
  5. <metahttp-equiv="content-type"content="text/html;charset=UTF-8">
  6. </head>
  7. <body>
  8. <inputtype="button"value="点击我"id="btn">
  9. </body>
  10. </html>
  11. <scripttype="text/javascript">
  12. <!--
  13. varEventHandler=function(){}
  14. EventHandler.prototype.handleEvent=function(event){
  15. alert('用对象作为事件句柄,只要实现该对象的方法handleEvent即可');
  16. alert(event.type);
  17. }
  18. varobjectHandler=newEventHandler();
  19. varbtn1Obj=document.getElementById("btn");
  20. if(window.addEventListener){
  21. btn1Obj.addEventListener("click",objectHandler,false);
  22. }elseif(window.attachEvent){
  23. //btn1Obj.attachEvent("onclick",objectHandler);//调用失败,说明不支持把对象注册为事件句柄
  24. //btn1Obj.attachEvent("onclick",objectHandler.handleEvent);
  25. registerObjectEventHandler(btn1Obj,"onclick",objectHandler);
  26. }
  27. /**
  28. *对于不支持把对象注册为事件句柄的浏览器,可以调用以下方法来实现
  29. */
  30. functionregisterObjectEventHandler(element,eventtype,listener,captures){
  31. if(window.addEventListener){
  32. element.addEventListener(eventtype,
  33. function(event){listener.handleEvent(event);},captures);
  34. }elseif(window.attachEvent){
  35. element.attachEvent(eventtype,
  36. function(event){listener.handleEvent(event);});
  37. }
  38. }
  39. //-->
  40. </script>

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值