封装了个弹窗组件

前阵子,自己尝试在github上push了自己封装的一个弹窗组件,其实也不全是自己做的,只是照着之前项目用的组件源码重构了一下。

原来的组件来自 dhtmlx

但看过其源码实现发现,它居然在弹窗框外层加了个span包裹,实在匪夷所思,当时项目封装组件也是我来弄,由于改不了源码,我就在弹窗dom字符串前后分别加入了</span><span>来规避掉外层的span,但想想还是挺恶心的。

于是后来想着,既然都看懂源码了,不如自己实现一个轮子,取其精华去其糟粕,整一个以后新项目可以用。

于是我就写了,然后push到github了,地址在这 github:dbox【注:为毛不叫pop.js呢,因为我之前写过一篇文章提到过关于popjs被浏览器或者adblock屏蔽的事

功能

重新审视了一下以往项目的需求,先总结了这次封装组件应到实现的功能,大致如下:

  1. 弹窗类型:alert, confirm, modbox 即弹框提示、具备确认取消按钮、自定义弹窗
  2. 弹窗位置居中,基本上触屏版本来空间就不多,所以就定位垂直跟水平居中了
  3. 弹窗遮罩层,用于突出弹窗感
  4. 关闭弹窗的逻辑
  5. 延迟关闭弹窗、圆角、按钮回调等功能

实现原理

大致如下:

  1. 类型作为样式类名之一参与管理dom的样式,同时加入suffix参数来做业务定制样式的hook
  2. 居中这个直接通过计算弹框实体宽度和屏幕宽度取一半即可得相对于屏幕左方偏移,通过absolute来设置定位,同理高度
  3. 遮罩层 ,原本考虑用直接父元素加背景即可,后来想这样不够灵活,于是还是设置与弹框同层兄弟节点的方式来设置遮罩
  4. 关闭弹窗:弹框内关闭按钮点击触发,或者设置遮罩层点击触发
  5. 要做到延迟关闭 ,那么把关闭弹窗封装成对象方法,这样可以统一调用,圆角要考虑比较多,涉及到按钮的放置,具体看代码
  6. 事件绑定:原本考虑过遮罩层复用,那么如果用实体绑定事件,调用一次就生成一次事件绑定,会造成事件回调重复,不过通过判断遮罩关闭时把对应的事件清空即可。

具体可以看代码事件,其实也不复杂:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
 var DBox = (function(){

     var PopObj = function(cfg){
     	cfg['type'] = cfg['type'] || 'alert';//confirm   modbox
       	cfg["dom"] = cfg["dom"] || '';
		cfg["width"] = cfg["width"] || '50%';
		cfg["suffix"] = cfg["suffix"] || 'default';
        
        this.init(cfg);
      };

        PopObj.prototype = {
            construtor:PopObj,
            //初始化并启动弹窗
            init:function(cfg){
             	this.args = cfg;
                  //创建弹窗主题
             	this.creatBox();
                  //显示遮罩层
             	this.showCover();
                  //绑定事件
             	this.bindEvent();
            },
            creatBox:function(){
           
            	var wrap = document.createElement('div'),
            		btnTmpl = '',
            		innerTmpl = '';

            	wrap.className = 'dbox-wrap dbox-'+ this.args.suffix;

            	wrap.className += ' dbox-'+this.args.type;

                  //通过dbox-radius来设定弹窗带圆角
                  if(this.args.isRadius){
                        wrap.className += ' dbox-radius';
                  }

                  //带上关闭按钮
            	if(this.args.showClose){
            		innerTmpl += '<span class="dclose-position dclose">×</span>';
            	}

                  //根据type类型不同构造不同的窗体内容
            	if(this.args.type==='alert'){
            		innerTmpl += '<p class="dtext">'
            		+ this.args.dom 
            		+'</p><div class="dbuttons"><button class="db-yes">确定</button></div>';
            	}

            	if(this.args.type==='confirm'){
            		innerTmpl += '<p class="dtext">'
            		+ this.args.dom 
            		+'</p><div class="dbuttons"><button class="db-no">取消</button><button class="db-yes">确定</button></div>';
            	}

            	if(this.args.type==='modbox'){
            		innerTmpl += this.args.dom;
            	}

            	if(this.args.width){
            		wrap.style.width = this.args.width;
            	}

            	wrap.innerHTML = innerTmpl;

            	document.body.appendChild(wrap);

            	//调整位置:动态居中
            	var x = Math.abs(Math.floor(((window.innerWidth||document.documentElement.offsetWidth) - wrap.offsetWidth)/2));
				var y = Math.abs(Math.floor(((window.innerHeight||document.documentElement.offsetHeight) - wrap.offsetHeight)/2));

				wrap.style.top = y +'px';
				wrap.style.left = x + 'px';


				this.box = wrap;
            },
            bindEvent:function(){
            	var self = this,
                        domsYes = document.getElementsByClassName('db-yes'),
                        domsNo = document.getElementsByClassName('db-no'),
                        domsClose = document.getElementsByClassName('dclose');
            	//是否延迟自动关闭
            	if(self.args.timeOut&&self.args.timeOut>0){
            		setTimeout(function(){
            			self.remove();	
            		}, self.args.timeOut);
            	}
            	//点击确定btn事件
            	domsYes.length&&(domsYes[0].onclick = function(){
            		if(self.args.yFn&&self.args.yFn()===false){
            			return;
            		}
            		self.remove();
            	});
            	//点击取消btn事件
            	domsNo.length&&(domsNo[0].onclick = function(){
            		if(self.args.nFn&&self.args.nFn()===false){
            			return;
            		}
            		self.remove();
            	});
            	//点击关闭按钮事件
            	domsClose.length&&(domsClose[0].onclick = function(){
            		self.remove();
            	});
            },
            showCover:function(){
            	var self = this;
            	var cover = document.getElementsByClassName('dbox-cover');
                  //当页面已经有cover存在,复用
            	if(cover&&cover.length){
            		[].forEach.call(cover,function(e,i){
            			//显示,并绑定点击事件
                              e.style.display = 'block';
                  		if(self.args.outClose){
                                    e.onclick = function(ev){
                                          self.remove();
                                    };
                              }
            		});
            		return;
            	}
            	cover = document.createElement('div');
            	cover.className = 'dbox-cover';

            	//是否点击遮罩关闭
            	if(self.args.outClose){
                  	cover.onclick = function(e){
                  		self.remove();
                  	};
                  }

            	document.body.insertBefore(cover,document.body.childNodes[0]);
            },
            hideCover:function(){
            	var cover = document.getElementsByClassName('dbox-cover');
            	if(cover&&cover.length){
            		[].forEach.call(cover,function(e,i){
            			e.style.display = 'none';
                              //关闭遮罩,取消点击事件
                              e.onclick = null;
            		});
            		return;
            	}
            },
            //关闭弹窗
            remove: function(){
            	if(this.box){
            		this.box.parentNode.removeChild(this.box);
            	}
            	this.hideCover();
                  //this = null;
            }
          };

     //return PopObj;
      var result = function(cfg){
            return new PopObj(cfg);
      };
      return result;
})();