很多DOM对象都有原生的事件支持,向div就有click、mouseover等事件,事件机制可以为类的设计带来很大的灵活性,相信.net程序员深有体会。随着web技术发展,使用JavaScript自定义对象愈发频繁,让自己创建的对象也有事件机制,通过事件对外通信,能够极大提高开发效率。

简单的事件需求

事件并不是可有可无,在某些需求下是必需的。以一个很简单的需求为例,在web开发中Dialog很常见,每个Dialog都有一个关闭按钮,按钮对应Dialog的关闭方法,代码看起来大概是这样

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>Test</title>
  5. <style type="text/css" >
  6. .dialog
  7. {
  8. position:fixed;
  9. width:300px;
  10. height:300px;

z-index:;
                top:50%; left:50%;

                margin-top:-200px; margin-left:-200px;

                box-shadow:2px 2px 4px #ccc;

                background-color:#f1f1f1;

                display:none;

}

           
            .dialog .title

{

                font-size:16px;

                font-weight:bold;

                color:#fff;

                padding:4px;

                background-color:#404040;

}

           
            .dialog .close

{

                width:20px;

                height:20px;

                margin:3px;

                float:right;

                cursor:pointer;

}

</style>

</head>

<body>

<inputtype="button" value="Dialog Test" onclick="openDialog();"/>

<divid="dlgTest" class="dialog">

<imgclass="close" alt="" src="data:images/close.png">

<divclass="title">Dialog</div>

<divclass="content">

</div>

</div>

<scripttype="text/javascript">

function Dialog(id){

           this.id=id;

           var that=this;

            document.getElementById(id).children[0].onclick=function(){

                that.close();

}

}

Dialog.prototype.show=function(){

           var dlg=document.getElementById(this.id);

            dlg.style.display='block';

            dlg=null;

        }

Dialog.prototype.close=function(){

           var dlg=document.getElementById(this.id);

            dlg.style.display='none';

            dlg=null;

        }

</script>

<scripttype="text/javascript">

function openDialog(){

           var dlg=new Dialog('dlgTest');

            dlg.show();

}

</script>

</body>

<html>

这样在点击button的时候就可以弹出Dialog,点击关闭按钮的时候隐藏Dialog,看起来不错实现了需求,但总感觉缺点儿什么,一般Dialog显示的时候页面还会弹出一层灰蒙蒙半透明的罩子,阻止页面其它地方的点击,Dialog隐藏的时候罩子去掉,页面又能够操作。加些代码添个罩子。

在body顶部添加一个pagecover

  1. <div id="pageCover" class="pageCover"></div>

为其添加style

  1. .pageCover
  2. {
  3. width:100%;
  4. height:100%;
  5. position:absolute;
  6. z-index:;
  7. background-color:#666;
  8. opacity:0.5;
  9. display:none;
  10. }

为了打开的时候显示page cover,需要修改openDialog方法

  1. function openDialog(){
  2. var dlg=new Dialog('dlgTest');
  3. document.getElementById('pageCover').style.display='block';
  4. dlg.show();
  5. }

效果很不错的样子,灰蒙蒙半透明的罩子在Dialog弹出后遮盖住了页面上的按钮,Dialog在其之上,这时候问题来了,关闭Dialog的时候page cover仍在,没有代码其隐藏它,看看打开的时候怎么显示的page cover,关闭的时候怎么隐藏行了! 还真不行,打开的代码是页面button按钮的事件处理程序自己定义的,在里面添加显示page cover的方法合情合理,但是关闭Dialog的方法是Dialog控件(虽然很简陋,远远算不上是控件)自己的逻辑,和页面无关,那修改Dialog的close方法可以吗?也不行!有两个原因,首先Dialog在定义的时候并不知道page cover的存在,这两个控件之间没有什么耦合关系,如果把隐藏page cover逻辑写在Dialog的close方法内,那么dialog是依赖于page cover的,也就是说页面上如果没有page cover,dialog就会出错。而且Dialog在定义的时候,也不知道特定页面的page cover id,没有办法知道隐藏哪个div,是不是在构造Dialog时把page cover id传入就可以了呢? 这样两个控件不再有依赖关系,也能够通过id查找到page cover DIV了,但是如果用户有的页面需要弹出page cover,有的不需要怎么办?

这是就事件大显身手的时候了,修改一下dialog 对象和openDialog方法

  1. function Dialog(id){
  2. this.id=id;
  3. this.close_handler=null;
  4. var that=this;
  5. document.getElementById(id).children[0].onclick=function(){
  6. that.close();
  7. if(typeof that.close_handler=='function')
  8. {
  9. that.close_handler();
  10. }
  11. }
  12. }
  1. function openDialog(){
  2. var dlg=new Dialog('dlgTest');
  3. document.getElementById('pageCover').style.display='block';
  4.  
  5. dlg.close_handler=function(){
  6. document.getElementById('pageCover').style.display='none';
  7. }
  8. dlg.show();
  9. }

在Dialog对象内部添加一个句柄,关闭按钮的click事件处理程序在调用close方法后判断该句柄是否为function,是的话就调用执行该句柄。在openDialog方法中,创建Dialog对象后对句柄赋值为一隐藏page cover方法,这样在关闭Dialog的时候就隐藏了page cover,同时没有造成两个控件之间的耦合。这一交互过程就是一个简单的 定义事件——绑定事件处理程序——触发事件的过程,DOM对象的事件,比如button的click事件也是类似原理。

高级一点的自定义事件

上面举的小例子很简单,远远不及DOM本身事件精细,这种简单的事件处理有很多弊端

1.没有共同性。如果在定义一个控件,还得写一套类似的结构处理

2.事件绑定有排斥性。只能绑定了一个close事件处理程序,绑定新的会覆盖之前绑定

3.封装不够完善。如果用户不知道有个 close_handler的句柄,就没有办法绑定该事件,只能去查源代码

逐个分析一下这几个弊端,弊端一很熟悉,使用过面向对象的同学都可以轻易想到解决方法——继承;对于弊端二则可以提供一个容器(二维数组)来统一管理所有事件;弊端三的解决需要和弊端一结合在自定义的事件管理对象中添加统一接口用于添加/删除/触发事件

  1. function EventTarget(){
  2. this.handlers={};
  3. }
  4.  
  5. EventTarget.prototype={
  6. constructor:EventTarget,
  7. addHandler:function(type,handler){
  8. if(typeof this.handlers[type]=='undefined'){
  9. this.handlers[type]=new Array();
  10. }
  11. this.handlers[type].push(handler);
  12. },
  13. removeHandler:function(type,handler){
  14. if(this.handlers[type] instanceof Array){
  15. var handlers=this.handlers[type];
  16. for(var i=0,len=handlers.length;i<len;i++){
  17. if(handler[i]==handler){
  18. handlers.splice(i,1);
  19. break;
  20. }
  21. }
  22. }
  23. },
  24. trigger:function(event){
  25. if(!event.target){
  26. event.target=this;
  27. }
  28. if(this.handlers[event.type] instanceof Array){
  29. var handlers=this.handlers[event.type];
  30. for(var i=0,len=handlers.length;i<len;i++){
  31. handlers[i](event);
  32. }
  33. }
  34. }
  35. }

addHandler方法用于添加事件处理程序,removeHandler方法用于移除事件处理程序,所有的事件处理程序在属性handlers中统一存储管理。调用trigger方法触发一个事件,该方法接收一个至少包含type属性的对象作为参数,触发的时候会查找handlers属性中对应type的事件处理程序。写段代码测试一下。

  1. function onClose(event){
  2. alert('message:'+event.message);
  3. }
  4.  
  5. var target=new EventTarget();
  6. target.addHandler('close',onClose);
  7.  
  8. //浏览器不能帮我们创建事件对象了,自己创建一个
  9. var event={
  10. type:'close',
  11. message:'Page Cover closed!'
  12. };
  13.  
  14. target.trigger(event);

至此后连个弊端一解决,应用一下继承解决第一个弊端,下面是寄生式组合继承的核心代码,这种继承方式是目前公认的JavaScript最佳继承方式

  1. function extend(subType,superType){
  2. var prototype=Object(superType.prototype);
  3. prototype.constructor=subType;
  4. subType.prototype=prototype;
  5. }

最后写成的版本就是这样的

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>Test</title>
  5. <style type="text/css" >
  6. html,body
  7. {
  8. height:100%;
  9. width:100%;
  10. padding:0;
  11. margin:0;
  12. }
  13.  
  14. .dialog
  15. {
  16. position:fixed;
  17. width:300px;
  18. height:300px;
  19. top:50%;
  20. left:50%;
  21. margin-top:-200px;
  22. margin-left:-200px;
  23. box-shadow:2px 2px 4px #ccc;
  24. background-color:#f1f1f1;
  25. z-index:30;
  26. display:none;
  27. }
  28.  
  29. .dialog .title
  30. {
  31. font-size:16px;
  32. font-weight:bold;
  33. color:#fff;
  34. padding:4px;
  35. background-color:#404040;
  36. }
  37.  
  38. .dialog .close
  39. {
  40. width:20px;
  41. height:20px;
  42. margin:3px;
  43. float:right;
  44. cursor:pointer;
  45. }
  46.  
  47. .pageCover
  48. {
  49. width:100%;
  50. height:100%;
  51. position:absolute;
  52. z-index:10;
  53. background-color:#666;
  54. opacity:0.5;
  55. display:none;
  56. }
  57. </style>
  58. </head>
  59. <body>
  60. <div id="pageCover" class="pageCover"></div>
  61.  
  62. <input type="button" value="Dialog Test" onclick="openDialog();"/>
  63.  
  64. <div id="dlgTest" class="dialog">
  65. <img class="close" alt="" src="data:images/close.png">
  66. <div class="title">Dialog</div>
  67. <div class="content">
  68.  
  69. </div>
  70. </div>
  71.  
  72. <script type="text/javascript">
  73. function EventTarget(){
  74. this.handlers={};
  75. }
  76.  
  77. EventTarget.prototype={
  78. constructor:EventTarget,
  79. addHandler:function(type,handler){
  80. if(typeof this.handlers[type]=='undefined'){
  81. this.handlers[type]=new Array();
  82. }
  83. this.handlers[type].push(handler);
  84. },
  85. removeHandler:function(type,handler){
  86. if(this.handlers[type] instanceof Array){
  87. var handlers=this.handlers[type];
  88. for(var i=0,len=handlers.length;i<len;i++){
  89. if(handler[i]==handler){
  90. handlers.splice(i,1);
  91. break;
  92. }
  93. }
  94. }
  95. },
  96. trigger:function(event){
  97. if(!event.target){
  98. event.target=this;
  99. }
  100. if(this.handlers[event.type] instanceof Array){
  101. var handlers=this.handlers[event.type];
  102. for(var i=0,len=handlers.length;i<len;i++){
  103. handlers[i](event);
  104. }
  105. }
  106. }
  107. }
  108. </script>
  109.  
  110. <script type="text/javascript">
  111. function extend(subType,superType){
  112. var prototype=Object(superType.prototype);
  113. prototype.constructor=subType;
  114. subType.prototype=prototype;
  115. }
  116. </script>
  117.  
  118. <script type="text/javascript">
  119. function Dialog(id){
  120. EventTarget.call(this)
  121. this.id=id;
  122. var that=this;
  123. document.getElementById(id).children[0].onclick=function(){
  124. that.close();
  125. }
  126. }
  127.  
  128. extend(Dialog,EventTarget);
  129.  
  130. Dialog.prototype.show=function(){
  131. var dlg=document.getElementById(this.id);
  132. dlg.style.display='block';
  133. dlg=null;
  134. }
  135.  
  136. Dialog.prototype.close=function(){
  137. var dlg=document.getElementById(this.id);
  138. dlg.style.display='none';
  139. dlg=null;
  140. this.trigger({type:'close'});
  141. }
  142. </script>
  143.  
  144. <script type="text/javascript">
  145. function openDialog(){
  146. var dlg=new Dialog('dlgTest');
  147.  
  148. dlg.addHandler('close',function(){
  149. document.getElementById('pageCover').style.display='none';
  150. });
  151.  
  152. document.getElementById('pageCover').style.display='block';
  153. dlg.show();
  154. }
  155. </script>
  156. </body>
  157. <html>

最后

这样解决了几个弊端看起来就完美多了,其实可以把打开Dialog显示page cover也写成类似关闭时事件的方式了。当代码中存在多个部分在特定时刻相互交互的情况下,自定义事件就非常有用了。如果每个对象都有其它对象的引用,那么整个代码高度耦合,对象改动会影响其它对象,维护起来困难重重。自定义事件使对象解耦,功能隔绝,这样对象之间实现了高聚合。

JavaScript自定义事件的更多相关文章

  1. Javascript事件模型系列(四)我所理解的javascript自定义事件

    被我拖延了将近一个月的javascript事件模型系列终于迎来了第四篇,也是我计划中的最后一篇,说来太惭愧了,本来计划一到两个星期写完的,谁知中间遇到了很多事情,公司的个人的,搞的自己心烦意乱浮躁了一 ...

  2. javascript:自定义事件初探

    javascript:自定义事件初探   http://www.cnblogs.com/jeffwongishandsome/archive/2008/10/27/1317148.html

  3. 理解的javascript自定义事件

    理解的javascript自定义事件 被我拖延了将近一个月的javascript事件模型系列终于迎来了第四篇,也是我计划中的最后一篇,说来太惭愧了,本来计划一到两个星期写完的,谁知中间遇到了很多事情, ...

  4. Javascript自定义事件功能与用法实例分析

    原文地址:https://www.jb51.net/article/127776.htm 本文实例讲述了javascript自定义事件功能与用法.分享给大家供大家参考,具体如下: 概述 自定义事件很难 ...

  5. javascript 自定义事件 发布-订阅 模式 Event

    * javascript自定义事件 var myEvent = document.createEvent("Event"); myEvent.initEvent("myE ...

  6. Javascript 自定义事件 (custom event)

    Javascript 中经常会用到自定义事件.如何创建一个简单的自定义事件呢?在创建自定义的事件之前,我们应该考虑一下和事件有关的东西.例如 click 事件,首先我们要能注册一个click事件(在一 ...

  7. JavaScript自定义事件 - createEvent()、initEvent()和dispachEvent()

    在学习目标事件的方法的时候,接触到了dispatchEvent()方法.度娘查一查,这是一个事件触发器,事件触发器其实就是触发事件的东西. 通常情况下,我们触发事件都是在交互中触发的事件,例如点击按钮 ...

  8. 高级功能:很有用的javascript自定义事件

    之前写了篇文章<原生javascript实现类似jquery on方法的行为监听>比较浅显,能够简单的使用场景. 这里的自定义事件指的是区别javascript默认的与DOM交互的事件,比 ...

  9. JavaScript自定义事件,动态添加属性

    根据事件的不同,可用的自定义方法也不同. document.createEvent('Event'); 实现主要有4个步骤: 1.创建事件. 2.初始化事件(三个参数:事件名,是否起泡,是否取消默认触 ...

随机推荐

  1. SQL中使用update inner join和delete inner join

    Update XXX set XXX where 这种写法大家肯定都知道,才发现update和delete居然支持inner join的update方式,太神奇了.分享段示例代码: 1 2 3 4 5 ...

  2. codevs4919 线段树练习4

    4919 线段树练习4  时间限制: 1 s  空间限制: 128000 KB       题目描述 Description 给你N个数,有两种操作 1:给区间[a,b]内的所有数都增加X 2:询问区 ...

  3. [Swift]基础

    [Swift]基础 一, 常用变量 var str = "Hello, playground" //变量 let str1="Hello xmj112288" ...

  4. collectionView使用细节

    1.//创建组头组尾一个方法 - (UICollectionReusableView *)stCollectionView:(UICollectionView *)collectionView vie ...

  5. Java web 之表单验证

    按照软件工程师的定位来讲,表单验证应该要好好练习的 html  javascript

  6. iOS页面间传值的方式(Delegate/NSNotification/Block/NSUserDefault/单例)

    iOS页面间传值实现方法:1.通过设置属性,实现页面间传值:2.委托delegate方式:3.通知notification方式:4.block方式:5.UserDefault或者文件方式:6.单例模式 ...

  7. iOS.ReactNative-4-react-native-command-line-tool

    Command line tool: react-native 1. react-native 是一个命令行工具 1.1 react-native简介 运行以下命令: ls -lt `which re ...

  8. 0731am视图 模型

    跨控制器调用方法 function DiaoYong(){ 造对象$sc = new \Home\Controller\GoodsController();echo $sc->aa(); 如果在 ...

  9. 前端利器:SASS基础与Compass入门

    SASS是Syntactically Awesome Stylesheete Sass的缩写,它是css的一个开发工具,提供了很多便利和简单的语法,让css看起来更像是一门语言,这种特性也被称为“cs ...

  10. css小技巧之去掉蓝色底块的方法

    -moz-user-select: none; /*火狐*/ -webkit-user-select: none; /*webkit浏览器*/ -ms-user-select: none; /*IE1 ...