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 相关文章推荐
如何将JS的变量值传递给ASP变量
Dec 10 Javascript
js实现拉伸拖动iframe的具体代码
Aug 03 Javascript
使用jquery插件qrcode生成二维码
Oct 22 Javascript
Javascript的表单验证-初识正则表达式
Mar 18 Javascript
js中数组的常用方法小结
Dec 30 Javascript
JS 实现计算器详解及实例代码(一)
Jan 08 Javascript
数组Array的排序sort方法
Feb 17 Javascript
AngularJS双向数据绑定原理之$watch、$apply和$digest的应用
Jan 30 Javascript
swiper 自动图片无限轮播实现代码
May 21 Javascript
详解Vue Elementui中的Tag与页面其它元素相互交互的两三事
Sep 25 Javascript
Node.js API详解之 dns模块用法实例分析
May 15 Javascript
解决vue props传Array/Object类型值,子组件报错的情况
Nov 07 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
变量在 PHP7 内部的实现(一)
2015/12/21 PHP
PHP策略模式定义与用法示例
2017/07/27 PHP
javascript来定义类的规范小结
2010/11/19 Javascript
使用JavaScript检测Firefox浏览器是否启用了Firebug的代码
2010/12/28 Javascript
(跨浏览器基础事件/浏览器检测/判断浏览器)经验代码分享
2013/01/24 Javascript
Extjs4 消息框去掉关闭按钮(类似Ext.Msg.alert)
2013/04/02 Javascript
js动态生成指定行数的表格
2013/07/11 Javascript
设为首页和收藏的Javascript代码(亲测兼容IE,Firefox,chrome等浏览器)
2013/11/18 Javascript
jquery的live使用注意事项
2014/02/18 Javascript
jQuery使用height()获取高度需要注意的地方
2014/12/13 Javascript
60行js代码实现俄罗斯方块
2015/03/31 Javascript
基于jQuery.Hz2Py.js插件实现的汉字转拼音特效
2015/05/07 Javascript
js鼠标点击图片切换效果代码分享
2015/08/26 Javascript
前端把html表格生成为excel表格的实例
2017/09/19 Javascript
react配置antd按需加载的使用
2019/02/11 Javascript
elementUI 动态生成几行几列的方法示例
2019/07/11 Javascript
Python基础之函数用法实例详解
2014/09/10 Python
Python计算字符宽度的方法
2016/06/14 Python
快速入门python学习笔记
2017/12/06 Python
Python中使用logging和traceback模块记录日志和跟踪异常
2019/04/09 Python
python使用协程实现并发操作的方法详解
2019/12/27 Python
Python+OpenCV实现将图像转换为二进制格式
2020/01/09 Python
基于python修改srt字幕的时间轴
2020/02/03 Python
英国最受欢迎的手表网站:Watch Shop
2016/10/21 全球购物
New Era英国官网:美国棒球帽品牌
2018/03/21 全球购物
BASIC HOUSE官方旗舰店:韩国著名的服装品牌
2018/09/27 全球购物
英国第一的滑雪服装和装备零售商:Snow+Rock
2020/02/01 全球购物
绘画设计学生的个人自我评价
2013/09/20 职场文书
怎样有效的进行自我评价
2013/10/06 职场文书
八年级音乐教学反思
2014/01/09 职场文书
《赠汪伦》教学反思
2014/04/12 职场文书
员工生日会策划方案
2014/06/14 职场文书
在校大学生自我评价范文
2014/09/12 职场文书
个人落户申请书怎么写?
2019/06/28 职场文书
Java 使用类型为Object的变量指向任意类型的对象
2022/04/13 Java/Android
springboot 全局异常处理和统一响应对象的处理方式
2022/06/28 Java/Android