设计弹框和遮罩层
<title>设计弹出对话框</title>
<style>
body{margin:0;padding: 0;}
.dialog{
width: 300px;height: 200px;
/*margin: 0 auto; 这样子居中会被遮罩层覆盖,对话框的X按钮将不可用,改用position: fixed;*/
position: fixed;left:50%;top: 50%;margin-top:-100px;margin-left: -150px;
background-color: #f1f1f1;border:solid 1px #aaa;border-radius: 4px;
overflow: hidden;box-shadow: 2px 2px 4px #CCCCCC;display: none;
z-index: 30;
}
.dialog .title{
padding: 6px;
font-size: 16px;font-weight: bold;color:#fff;background-color: #404040;
}
.dialog .close{
float:right;width: 20px;height: 20px;
font-size:20px;color:#fff;cursor: pointer;
}
.pageCover{
position: absolute;width:100%;height: 100%;
background-color: #666;opacity: 0.1;display: none;
z-index: 10;
}
</style>
</head>
<body>
<div id="pageCover" class="pageCover"></div>
<input type="button" value="打开对话框" onclick="openDialog();"/>
<div id="dlgTest" class="dialog">
<span class="close">×</span>
<div class="title">
对话宽标题
</div>
<div class="content">
对话框内容
</div>
</div>
<script type="text/javascript">
//1.函数写法
// var odig=document.getElementById("dlgTest");
// var cdig=document.getElementsByClassName("close")[0];
// function openDialog(){
// odig.style.display="block";
// }
// cdig.onclick=function(){
// odig.style.display="none";
// }
//2.对象写法
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(){
// document.getElementById("pageCover").style.display="none"; 不能再这写这句代码
//因为在定义Dialog时并不知道遮罩层的存在,对话框和遮罩没有耦合关系,如果把关闭遮罩的逻辑写在Dialog的close方法内,
//那么会造成Dialog依赖于遮罩,如果页面没有遮罩时, Dialog就会出错(它的close方法中找不到遮罩,自然报错)
var dlg=document.getElementById(this.id);
dlg.style.display="none";
dlg=null;
}
function openDialog(){
document.getElementById("pageCover").style.display="block";var dlg=new Dialog("dlgTest");
dlg.show();
}
</script>
</body>
定义时间雏形:(那么怎么关闭遮罩层?)
重写对话框类型对象
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=="funciton"){
this.close_handler();
}
}
}//重写打开对话框的方法
function openDialog(){
document.getElementById("pageCover").style.display="block";
var dlg=new Dialog("dlgTest");
dlg.show();
dlg.close_handler=function(){//注册事件,为句柄(本地属性)传递一个事件处理函数
document.getElementById("pageCover").style.display="none";
}
}在openDialog方法中,创建Dialog对象后为句柄赋值,传递一个隐藏遮罩的方法,这样在关闭Dialog时就隐藏了遮罩,同时没有造成两个组件之间耦合
自定义事件:即先绑定事件处理程序,然后在原生事件处理函数中调用,以实现触发的过程。DOM对象的事件,如button的click事件,也是类似原理
设计事件触发模型;
//使用观察者模式实现事件监听,自定义事件类型
function EventTarget(){
this.handlers={};//初始本地句柄为空
}
//扩展自定义事件类型的原型
EventTarget.prototype={
constructor:EventTarget,//修复EventTarget构造器本身
addHandler:function(type,handler){//注册事件:type表示事件类型 handler表示事件处理函数
if(typeof this.handlers[type]=='undefined'){//如果没有注册指定类型事件,则初始化为空数组
this.handlers[type]=new Array();
}
this.handlers[type].push(handler);//把当前事件处理函数推入到当前事件类型句柄队列的尾部
},
removeHandler:function(type,handler){//销毁事件:type表示事件类型 handler表示事件处理函数
if(this.handlers[type] instanceof Array){
var handlers=this.handlers[type];
for(var i=0,len=handlers.length;i<len;i++){
if(handlers[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);//逐一调用队列中每个事件处理函数,并把参数event传递给它
}
}
}
}
//测试自定义事件的添加和触发过程
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);
//寄生式组合继承,javas最佳继承方式,解决 没有共同性的弊端
function extend(subType,superType){
var prototype=Object(superType.prototype);
prototype.constructor=subType;
subType.prototype=prototype;
}
应用事件模型:优化后的总体代码
<head>
<meta charset="UTF-8">
<title>设计弹出对话框</title>
<style>
body{margin:0;padding: 0;}
.dialog{
width: 300px;height: 200px;
/*margin: 0 auto; 这样子居中会被遮罩层覆盖,对话框的X按钮将不可用,改用position: fixed;*/
position: fixed;left:50%;top: 50%;margin-top:-100px;margin-left: -150px;
background-color: #f1f1f1;border:solid 1px #aaa;border-radius: 4px;
overflow: hidden;box-shadow: 2px 2px 4px #CCCCCC;display: none;
z-index: 30;
}
.dialog .title{
padding: 6px;
font-size: 16px;font-weight: bold;color:#fff;background-color: #404040;
}
.dialog .close{
float:right;width: 20px;height: 20px;
font-size:20px;color:#fff;cursor: pointer;
}
.pageCover{
position: absolute;width:100%;height: 100%;
background-color: #666;opacity: 0.1;display: none;
z-index: 10;
}
</style>
</head>
<body>
<div id="pageCover" class="pageCover"></div>
<input type="button" value="打开对话框" onclick="openDialog();"/>
<div id="dlgTest" class="dialog">
<span class="close">×</span>
<div class="title">
对话宽标题
</div>
<div class="content">
对话框内容
</div>
</div><script>
function EventTarget(){
this.handlers={};//初始本地句柄为空
}
//扩展自定义事件类型的原型
EventTarget.prototype={
constructor:EventTarget,//修复EventTarget构造器本身
addHandler:function(type,handler){//注册事件:type表示事件类型 handler表示事件处理函数
if(typeof this.handlers[type]=='undefined'){//如果没有注册指定类型事件,则初始化为空数组
this.handlers[type]=new Array();
}
this.handlers[type].push(handler);//把当前事件处理函数推入到当前事件类型句柄队列的尾部
},
removeHandler:function(type,handler){//销毁事件:type表示事件类型 handler表示事件处理函数
if(this.handlers[type] instanceof Array){
var handlers=this.handlers[type];
for(var i=0,len=handlers.length;i<len;i++){
if(handlers[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);//逐一调用队列中每个事件处理函数,并把参数event传递给它
}
}
}
}
//寄生式组合继承,javas最佳继承方式,解决 没有共同性的弊端
function extend(subType,superType){
var prototype=Object(superType.prototype);
prototype.constructor=subType;
subType.prototype=prototype;
}
//定义对话框类型
function Dialog(id){
EventTarget.call(this);//动态调用EventTarget类型函数,继承它的本地成员
this.id=id;
var that=this;
document.getElementById(id).children[0].onclick=function(){
that.close();
}
}
//继承EventTarget类型原型属性
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'})
}
function openDialog(){
document.getElementById("pageCover").style.display="block";
var dlg=new Dialog("dlgTest");
dlg.show();
dlg.addHandler('close',function(){//为当前实例注册close事件,并传递事件函数
document.getElementById("pageCover").style.display="none";
})
}
</script>这样用户也可以在打开dialog时,显示遮罩写成类似关闭事件的方式。当代码中存在多个部分,在特定时刻相互交互的情况下,自定义事件就非常有用
如果每个对象都有其他对象的引用,那么整个代码高度耦合,对象改动会影响其他对象,维护起来就困难重重,自定义事件能够解耦,功能隔绝,这样对象之间就可以实现高度聚合