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 相关文章推荐
javascript 函数使用说明
Apr 07 Javascript
js格式化货币数据实现代码
Sep 04 Javascript
JS小游戏之象棋暗棋源码详解
Sep 25 Javascript
javascript使用正则表达式检测IP地址
Dec 03 Javascript
JavaScript中的原型继承基础学习教程
May 06 Javascript
微信小程序开发之选项卡(窗口底部TabBar)页面切换
Apr 12 Javascript
浅析vue深复制
Jan 29 Javascript
Vue 项目代理设置的优化
Apr 17 Javascript
vue实现微信分享链接添加动态参数的方法
Apr 29 Javascript
微信小程序引入模块中wxml、wxss、js的方法示例
Aug 09 Javascript
js对象数组和对象的使用实例详解
Aug 27 Javascript
vue 手机物理监听键+退出提示代码
Sep 09 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
PHPExcel 修改已存在Excel的方法
2018/05/03 PHP
JavaScript中关于indexOf的使用方法与问题小结
2010/08/05 Javascript
dreamweaver 安装Jquery智能提示
2011/04/02 Javascript
JavaScript高级程序设计 阅读笔记(十八) js跨平台的事件
2012/08/14 Javascript
javascript 中String.match()与RegExp.exec()的区别说明
2013/01/10 Javascript
利用js 进行输入框自动匹配字符的小例子
2013/06/29 Javascript
可恶的ie8提示缺少id未定义
2014/03/20 Javascript
js判断当前浏览器类型,判断IE浏览器方法
2014/06/02 Javascript
整理AngularJS中的一些常用指令
2015/06/16 Javascript
javascript实现动态表头及表列的展现方法
2015/07/14 Javascript
onmouseover事件和onmouseout事件全面理解
2016/08/15 Javascript
vue.js父组件使用外部对象的方法示例
2017/04/25 Javascript
一个可复用的vue分页组件
2017/05/15 Javascript
JavaScript实现打印星型金字塔功能实例分析
2017/09/27 Javascript
SpringBoot+Vue前后端分离,使用SpringSecurity完美处理权限问题的解决方法
2018/01/09 Javascript
微信小程序实现流程进度的图样式功能
2018/01/16 Javascript
详解Vue webapp项目通过HBulider打包原生APP
2018/06/29 Javascript
js实现验证码干扰(静态)
2021/02/22 Javascript
[51:27]LGD vs Liquid 2019国际邀请赛小组赛 BO2 第二场 8.16
2019/08/19 DOTA
python解析xml模块封装代码
2014/02/07 Python
python中logging库的使用总结
2017/10/18 Python
Python内置模块turtle绘图详解
2017/12/09 Python
python生成密码字典的方法
2018/07/06 Python
Python数据类型之Number数字操作实例详解
2019/05/08 Python
详解将Pandas中的DataFrame类型转换成Numpy中array类型的三种方法
2019/07/06 Python
Python实现简单的列表冒泡排序和反转列表操作示例
2019/07/10 Python
图解python全局变量与局部变量相关知识
2019/11/02 Python
python GUI库图形界面开发之PyQt5信号与槽的高级使用技巧装饰器信号与槽详细使用方法与实例
2020/03/06 Python
Prometheus开发中间件Exporter过程详解
2020/11/30 Python
python如何构建mock接口服务
2021/01/28 Python
html5的websockets全双工通信详解学习示例
2014/02/26 HTML / CSS
Origins加拿大官网:雅诗兰黛集团高端植物护肤品牌
2017/11/19 全球购物
英国Boots旗下太阳镜网站:Boots Designer Sunglasses
2018/07/07 全球购物
CLR与IL分别是什么含义
2016/08/23 面试题
食品流通安全承诺书
2014/05/22 职场文书
信仰纪录片观后感
2015/06/08 职场文书