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

简单的事件需求

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

<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<style type="text/css" >
.dialog
{
position:fixed;
width:300px;
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

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

为其添加style

.pageCover
{
width:100%;
height:100%;
position:absolute;
z-index:;
background-color:#666;
opacity:0.5;
display:none;
}

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

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

效果很不错的样子,灰蒙蒙半透明的罩子在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方法

function Dialog(id){
this.id=id;
this.close_handler=null;
var that=this;
document.getElementById(id).children[0].onclick=function(){
that.close();
if(typeof that.close_handler=='function')
{
that.close_handler();
}
}
}
function openDialog(){
var dlg=new Dialog('dlgTest');
document.getElementById('pageCover').style.display='block'; dlg.close_handler=function(){
document.getElementById('pageCover').style.display='none';
}
dlg.show();
}

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

高级一点的自定义事件

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

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

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

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

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

function EventTarget(){
this.handlers={};
} EventTarget.prototype={
constructor:EventTarget,
addHandler:function(type,handler){
if(typeof this.handlers[type]=='undefined'){
this.handlers[type]=new Array();
}
this.handlers[type].push(handler);
},
removeHandler:function(type,handler){
if(this.handlers[type] instanceof Array){
var handlers=this.handlers[type];
for(var i=0,len=handlers.length;i<len;i++){
if(handler[i]==handler){
handlers.splice(i,1);
break;
}
}
}
},
trigger:function(event){
if(!event.target){
event.target=this;
}
if(this.handlers[event.type] instanceof Array){
var handlers=this.handlers[event.type];
for(var i=0,len=handlers.length;i<len;i++){
handlers[i](event);
}
}
}
}

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

function onClose(event){
alert('message:'+event.message);
} var target=new EventTarget();
target.addHandler('close',onClose); //浏览器不能帮我们创建事件对象了,自己创建一个
var event={
type:'close',
message:'Page Cover closed!'
}; target.trigger(event);

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

function extend(subType,superType){
var prototype=Object(superType.prototype);
prototype.constructor=subType;
subType.prototype=prototype;
}

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

<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<style type="text/css" >
html,body
{
height:100%;
width:100%;
padding:0;
margin:0;
} .dialog
{
position:fixed;
width:300px;
height:300px;
top:50%;
left:50%;
margin-top:-200px;
margin-left:-200px;
box-shadow:2px 2px 4px #ccc;
background-color:#f1f1f1;
z-index:30;
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;
} .pageCover
{
width:100%;
height:100%;
position:absolute;
z-index:10;
background-color:#666;
opacity:0.5;
display:none;
}
</style>
</head>
<body>
<div id="pageCover" class="pageCover"></div> <input type="button" value="Dialog Test" onclick="openDialog();"/> <div id="dlgTest" class="dialog">
<img class="close" alt="" src="data:images/close.png">
<div class="title">Dialog</div>
<div class="content"> </div>
</div> <script type="text/javascript">
function EventTarget(){
this.handlers={};
} EventTarget.prototype={
constructor:EventTarget,
addHandler:function(type,handler){
if(typeof this.handlers[type]=='undefined'){
this.handlers[type]=new Array();
}
this.handlers[type].push(handler);
},
removeHandler:function(type,handler){
if(this.handlers[type] instanceof Array){
var handlers=this.handlers[type];
for(var i=0,len=handlers.length;i<len;i++){
if(handler[i]==handler){
handlers.splice(i,1);
break;
}
}
}
},
trigger:function(event){
if(!event.target){
event.target=this;
}
if(this.handlers[event.type] instanceof Array){
var handlers=this.handlers[event.type];
for(var i=0,len=handlers.length;i<len;i++){
handlers[i](event);
}
}
}
}
</script> <script type="text/javascript">
function extend(subType,superType){
var prototype=Object(superType.prototype);
prototype.constructor=subType;
subType.prototype=prototype;
}
</script> <script type="text/javascript">
function Dialog(id){
EventTarget.call(this)
this.id=id;
var that=this;
document.getElementById(id).children[0].onclick=function(){
that.close();
}
} extend(Dialog,EventTarget); 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;
this.trigger({type:'close'});
}
</script> <script type="text/javascript">
function openDialog(){
var dlg=new Dialog('dlgTest'); dlg.addHandler('close',function(){
document.getElementById('pageCover').style.display='none';
}); document.getElementById('pageCover').style.display='block';
dlg.show();
}
</script>
</body>
<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. xfire webServeic 例子

    xfire webServeic 例子,参考网上众多例子,自己写得完成了,给大家分享 大家只要按这个目录去建文件就可以了,然后运行,至于其中原理慢慢理会吧 环境:myeclipse 10 +xfire ...

  2. swift 代码添加image

    let image_ElectricianBtn = UIImage(named: "ElectricianBtn") let vimage_ElectricianBtn = UI ...

  3. Looping Techniques

    [Looping Techniques] 1.When looping through dictionaries, the key and corresponding value can be ret ...

  4. The certificate used to sign “AppName” has either expired or has been revoked. An updated certificate is required to sign and install the application解决

    问题 The certificate used to sign "AppName" has either expired or has been revoked. An updat ...

  5. eclipse新建项目,报错“Error: workspace\appcompat_v7\res\values-v21\styles_base.xml No resource found that matches the given name”

    新建项目报错,不知道为什么,以前从未出现过的错误,把sdk更新之后,出现莫名错误,自己也是一知半解,在网上找了好久的错误,终于在一个english网站找到了解决方法,soga,从未觉得english如 ...

  6. linux特殊符号大全

    #   ;   ;;      .      ,       /       \       'string'|       !   $   ${}   $?      $$   $*  " ...

  7. R语言--数据预处理

    一.日期时间.字符串的处理 日期 Date: 日期类,年与日 POSIXct: 日期时间类,精确到秒,用数字表示 POSIXlt: 日期时间类,精确到秒,用列表表示 Sys.date(), date( ...

  8. 使用12c的DBCA创建数据库的时候报错TNS-04404

    情况:之前使用的默认的pdb数据库pdborcl,连接的时候使用SQLdeveloper,配置了tnsname.ora,里面添加了pdborcl的service:这里报错是感觉tnsname配置错误了 ...

  9. 【Java】XML解析之JDOM

    JDOM介绍 JDOM是一个开源项目,它基于树型结构,利用纯JAVA的技术对XML文档实现解析.生成.序列化以及多种操作.使用jdom需要引入jdom.jar包. XML生成及解析 代码如下: pac ...

  10. android压力测试命令monkey详解

    一.Monkey 是什么?Monkey 就是SDK中附带的一个工具. 二.Monkey 测试的目的?:该工具用于进行压力测试. 然后开发人员结合monkey 打印的日志 和系统打印的日志,结局测试中出 ...