JavaScript自定义事件介绍


Posted in Javascript onAugust 29, 2013

很多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:30; 
                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="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:10;
                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();
        }

JavaScript自定义事件介绍

效果很不错的样子,灰蒙蒙半透明的罩子在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="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 相关文章推荐
jQuery中RadioButtonList的功能及用法实例介绍
Aug 23 Javascript
JQuery实现带排序功能的权限选择实例
May 18 Javascript
Bootstrap每天必学之警告框插件
Apr 26 Javascript
jQuery实现的动态文字变化输出效果示例【附演示与demo源码下载】
Mar 24 jQuery
Node+Express+MongoDB实现登录注册功能实例
Apr 23 Javascript
Vue.js进行查询操作的实例详解
Aug 25 Javascript
react 父组件与子组件之间的值传递的方法
Sep 14 Javascript
JavaScript动态添加数据到表单并提交的几种方式
Jun 26 Javascript
JS实现压缩上传图片base64长度功能
Dec 03 Javascript
JavaScript实现Tab选项卡切换
Feb 13 Javascript
vue项目中微信登录的实现操作
Sep 08 Javascript
javascript实现随机抽奖功能
Dec 30 Javascript
JavaScript包装对象使用介绍
Aug 29 #Javascript
JavaScript作用域链使用介绍
Aug 29 #Javascript
JavaScript 命名空间 使用介绍
Aug 29 #Javascript
JavaScript prototype 使用介绍
Aug 29 #Javascript
JavaScript创建对象的写法
Aug 29 #Javascript
jQuery实现用户注册的表单验证示例
Aug 28 #Javascript
Jquery实现显示和隐藏的4种简单方式
Aug 28 #Javascript
You might like
PHP最常用的2种设计模式工厂模式和单例模式介绍
2012/08/14 PHP
PHP网页游戏学习之Xnova(ogame)源码解读(三)
2014/06/23 PHP
PHP的关于变量和日期处理的一些面试题目整理
2015/08/10 PHP
php 可变函数使用小结
2018/06/12 PHP
php设计模式之备忘模式分析【星际争霸游戏案例】
2020/03/24 PHP
让JavaScript 轻松支持函数重载 (Part 1 - 设计)
2009/08/04 Javascript
FileUpload 控件 禁止手动输入或粘贴的实现代码
2010/04/07 Javascript
什么是DOM(Document Object Model)文档对象模型
2012/03/05 Javascript
深入理解JavaScript系列(15) 函数(Functions)
2012/04/12 Javascript
node.js中的fs.createWriteStream方法使用说明
2014/12/17 Javascript
jQuery Ajax页面局部加载方法汇总
2016/06/02 Javascript
js实现常用排序算法
2016/08/09 Javascript
js滚轮事件兼容性问题需要注意哪些
2016/11/15 Javascript
jQuery File Upload文件上传插件使用详解
2016/12/06 Javascript
微信小程序 request接口的封装实例代码
2017/04/26 Javascript
JavaScript函数表达式详解及实例
2017/05/05 Javascript
node.js中grunt和gulp的区别详解
2017/07/17 Javascript
javscript 数组扁平化的实现
2020/02/03 Javascript
Python获取邮件地址的方法
2015/07/10 Python
12步入门Python中的decorator装饰器使用方法
2016/06/20 Python
python里使用正则的findall函数的实例详解
2017/10/19 Python
Python 普通最小二乘法(OLS)进行多项式拟合的方法
2018/12/29 Python
python抖音表白程序源代码
2019/04/07 Python
Django自定义用户登录认证示例代码
2019/06/30 Python
基于python实现获取网页图片过程解析
2020/05/11 Python
python读写数据读写csv文件(pandas用法)
2020/12/14 Python
BSTN意大利:德国街头和运动文化高品质商店
2020/12/22 全球购物
意大利在线高尔夫商店:Online Golf
2021/03/09 全球购物
测试驱动开发的主要步骤是什么
2014/12/10 面试题
日语求职信范文
2013/12/17 职场文书
竞选演讲稿范文
2013/12/28 职场文书
《颐和园》教学反思
2014/02/26 职场文书
超市创意活动方案
2014/08/15 职场文书
幼儿教师继续教育培训心得体会
2016/01/19 职场文书
2019年最新七夕唯美祝福语(60条)
2019/07/22 职场文书
详解nginx进程锁的实现
2021/06/14 Servers