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 相关文章推荐
HTML5附件拖拽上传drop &amp; google.gears实现代码
Apr 28 Javascript
JQuery操作Select的Options的Bug(IE8兼容性视图模式)
Apr 21 Javascript
jQuery中extend函数详解
Feb 13 Javascript
JavaScript Sort 的一个错误用法示例
Mar 20 Javascript
JS如何判断是否为ie浏览器的方法(包括IE10、IE11在内)
Dec 13 Javascript
AngularJS通过$location获取及改变当前页面的URL
Sep 23 Javascript
JS html时钟制作代码分享
Mar 03 Javascript
详解Vue-cli代理解决跨域问题
Sep 27 Javascript
浅谈angularJs函数的使用方法(大小写转换,拷贝,扩充对象)
Oct 08 Javascript
vue+element项目中过滤输入框特殊字符小结
Aug 07 Javascript
jQuery实现数字华容道小游戏(实例代码)
Jan 16 jQuery
深入了解JavaScript词法作用域
Jul 29 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+Mysql基于事务处理实现转账功能的方法
2015/07/08 PHP
JSON PHP中,Json字符串反序列化成对象/数组的方法
2018/05/31 PHP
基于Jquery的开发个代阴影的对话框效果代码
2011/07/28 Javascript
JS中把字符转成ASCII值的函数示例代码
2013/11/21 Javascript
js浮点数精确计算(加、减、乘、除)
2013/12/26 Javascript
javascript页面倒计时实例
2015/07/25 Javascript
JS实现数组去重方法总结(六种方法)
2017/07/14 Javascript
在一个页面实现两个zTree联动的方法
2017/12/20 Javascript
vue2.0 兄弟组件(平级)通讯的实现代码
2018/01/15 Javascript
Node.Js中实现端口重用原理详解
2018/05/03 Javascript
JS 实现分页打印功能
2018/05/16 Javascript
Bootstrap 按钮样式与使用代码详解
2018/12/09 Javascript
详解Vue3.0 前的 TypeScript 最佳入门实践
2019/06/18 Javascript
解决使用layui的时候form表单中的select等不能渲染的问题
2019/09/18 Javascript
vue引入静态js文件的方法
2020/06/20 Javascript
原生JS实现九宫格抽奖
2020/09/13 Javascript
小程序实现左滑删除的效果的实例代码
2020/10/19 Javascript
[02:06]DOTA2英雄基础教程 暗影萨满
2013/12/16 DOTA
[04:42]2015国际邀请赛CDEC战队晋级之路
2015/08/13 DOTA
获取python文件扩展名和文件名方法
2018/02/02 Python
python 在某.py文件中调用其他.py内的函数的方法
2019/06/25 Python
解决python中用matplotlib画多幅图时出现图形部分重叠的问题
2019/07/07 Python
Python之虚拟环境virtualenv,pipreqs生成项目依赖第三方包的方法
2019/07/23 Python
快速解决vue.js 模板和jinja 模板冲突的问题
2019/07/26 Python
简单了解为什么python函数后有多个括号
2019/12/19 Python
python通过opencv实现图片裁剪原理解析
2020/01/19 Python
工业设计专业推荐信
2013/10/29 职场文书
亲子活动总结
2014/04/26 职场文书
服装设计专业求职信
2014/06/16 职场文书
节电标语大全
2014/06/23 职场文书
2014年保卫工作总结
2014/12/05 职场文书
先进基层党组织事迹材料
2014/12/25 职场文书
高中教师个人总结
2015/02/10 职场文书
小学英语教学随笔
2015/08/14 职场文书
终止合同协议书范本
2016/03/22 职场文书
分析MySQL抛出异常的几种常见解决方式
2021/05/18 MySQL