从本篇开始,我们就开始手写自己网站独特风格的组件。由于弹出框在网页用到很多,而且与用户具有良好的交互性,因此弹出框在设计、实现及展示上必须花费很大功夫。
目录
在线访问链接:链接
源代码下载:链接
js依赖于zq.js:在线访问
在写前端组件的时候,我们要明确一个组件都需要什么。首先我们写弹框的时候会发现很多样式和事件可以公用,我们就可以把这些公共部分封装成模板或者在js里动态装配(推荐),基于装配过程我们可以封装一些内部方法。然后暴露出不公共的部分封装成默认配置项和配置项,并将配置项暴露给开发者。然后将装配过程的一些对象返回给开发者,让其定制自己的样式或者事件。这样,我们根据配置项就能生成想要的组件和动态绑定事件。
1、自定义自己的弹框
1、简单的提示框:
html代码:
<div class="panel-dialog-tips-base">
<div class="panel-dialog-tips-base-msg">
<span>普通的提示框:新增失败新增失败新增失败新增失败新增失败新增失败新增失败!</span>
</div>
</div>
css样式如下:
.panel-dialog-tips-base {
min-height: 2rem;
max-width: 16rem;
text-align: center;
top: 25%;
line-height: 2rem;
}
.panel-dialog-tips-base-msg {
background: rgba(0, 0, 0, 0.6);
border-radius: 5px;
max-width: 16rem;
padding: 0 2rem;
}
.panel-dialog-tips-base-msg span {
color: #fff;
display: inline-block;
}
上图的html是很简单的,公共部分是外层两个div跟span标签,我们可以在js里自动装配它,唯一不同的内容就只有span标签的内容即弹出的内容,所以就可以作为配置项或者传参的内容。当然我们这个提示框没有关闭按钮,所以我们可以将弹出时间作为传参传入。这样的话,我们的配置项就是传入文本跟显示时间。默认配置项为显示时间为2s,即不传弹出时间默认2s关闭。
如果要在整个页面弹出,我们需要修改一下样式,为了区分上面:
.dialog-tips-base {
min-height: 2rem;
text-align: center;
position: fixed;/*弹框是绝对绝对定位居中的*/
top: 35%;
line-height: 2rem;
width: 100%;
}
.dialog-tips-base-msg {
background: rgba(0, 0, 0, 0.6);
border-radius: 5px;
margin: auto;
padding: 0 2rem;
max-width: 16rem;
}
.dialog-tips-base-msg span {
color: #fff;
display: inline-block;
}
最后装配js是我最擅长的了,我们这次先以传参的形式去弹出,因为后面会有更复杂的弹框,只有我们做到最复杂的弹框的时候,才去写配置项,这是为了防止频繁的修改逻辑和没有必要的拓展,参数多了还要去适配前面简单的。所以我们做出较复杂的弹框后,才去抽取配置项跟事件:
/**
* 弹出提示框
* @param {Object} msg 提示框的内容
* @param {Object} t 提示框显示时间,默认2s
*/
function openBaseDialog1(msg, t) {
$(".dialog").remove();//先清空页面的提示框
var $dialog = $("<div></div>").addClass("dialog-tips-base dialog").append($("<div></div>").addClass(
"dialog-tips-base-msg").append($("<span></span>").append(msg)));
$dialog.appendTo($("body"))
setTimeout(function() {
$dialog.remove()
}, t || 2000)
}
//或者function openBaseDialog1(msg, t=2000){...}
2、带配置项的提示框
稍微拓展一下html就可以了:
<div class="panel-dialog-tips-base">
<div class="panel-dialog-tips-base-msg">
<span>普通的提示框:新增失败!</span>
<div class="panel-dialog-tips-base-btn">
<a class="dialog-tips-base-btn-btn1">好的</a>
<a class="dialog-tips-base-btn-btn2 close-dialog">取消</a>
</div>
</div>
</div>
拓展的css如下:
.panel-dialog-tips-base-btn {
display: flex;
justify-content: center;
}
.panel-dialog-tips-base-btn a {
display: block;
color: var(--primary);
width: 33%;
height: 28px;
line-height: 25px;
text-decoration: none;
}
这个我们就可以封装成配置项了,因为提示框可能再也做不出什么花样来了。不公共的部分还是弹出内容,弹出时间,弹出按钮,弹出按钮的文字即其点击事件,我们就可以这么封装:
{
msg:"提示框文本",//提示框文本
time:2,//弹出时间
btn:[//按钮
//第一个按钮配置项,
{
btn_text: "好的",//按钮文本
btn_type:"cancel",//按钮类型
btn_fn: function() {
alert("关闭");//按钮点击事件
},
}
]
}
//默认配置项
{
"time":2,
btn:[{
btn_text: "取消",
btn_fn: function() {
closeDialog();
},
}
]
}
最后就是根据配置项跟默认配置项装配我们的弹框了,这边我在按钮处也定制了一个默认的配置,当btn_type为cancel时,加载默认的取消按钮的配置项。js代码如下:
function openBaseDialog2(config) {
//定义默认配置项
var defaultConfig = {
time:2000,
btn:[
{
btn_text: "取消",
btn_fn: function() {
closeDialog();
},
}
]
};
//传入的配置与默认配置项生成一个新的配置项,两者不同的字段都会生成在新配置项内,相同的字段又传入的覆盖默认的。
var config = $.extend({}, defaultConfig, config);
$(".dialog").remove();//只能弹出一个提示框,在弹出前将其他提示框移除
var $dialog_btn = $("<div></div>").addClass("panel-dialog-tips-base-btn");
$.each(config.btn,function(index,value){
if(value.btn_type=="cancel"){//使用取消按钮的配置项
value = {
btn_text: "取消",
btn_fn: function() {
closeDialog();
},
}
}
$("<a></a>").append(value.btn_text).off("click").on("click", value.btn_fn).appendTo($dialog_btn);
})
//装配,把每一个共有的dom元素对象暴露出来,方便拓展
var $dialog_span = $("<span></span>").append(config.msg);
var $dialog_msg = $("<div></div>").addClass("dialog-tips-base-msg").append($dialog_span).append($dialog_btn);
var $dialog = $("<div></div>").addClass("dialog-tips-base dialog").append($dialog_msg);
$dialog.appendTo($("body"))
setTimeout(function() {
$dialog.remove()
}, config.time)
}
//内部方法,立刻关闭弹框
function closeDialog() {
$(".dialog").remove();
}
最后,可以将刚开始那个简单的提示框加入配置项管理。 这里,有的童靴可能会想到关闭弹框用remove会不会很暴力,你的这个想法非常不错。因为我们的弹框的dom是在js动态装配跟操作的,比较耗时,所以每次弹框都去这么生成就很不好。这里关闭弹框我们可以将其隐藏,打开新弹框前只需在隐藏的弹框内改动较少的内容然后展示即可,稍后我将在带有查询功能的组件里用到这个思想。
3、较复杂的弹出框
首先我们看到的弹出框一般分三部分组成:头部,身体跟尾部。头部主要是标题,关闭的x,或者再加上最小化跟最大化,还有一个拖拉拽事件跟放缩事件。其中可加入配置项的部分是标题;头部样式;关闭x前回调;是否可以最小化最大化及其回调;是否可以拖拽;是否可以上下伸展;身体主要是内容,这方面有很多种情况,我们就可以预设一些默认的情况,通过在配置项加入类型来去加载不同的预设的一些内容。尾部就是最下面,一般是按钮,配置项就是按钮的一些样式跟回调。
<div class="modal-dialog" style="margin: 0;width: 100%;">
<div class="modal-content">
<div class="modal-header"><button class="close"><span>×</span></button><span
class="modal-title">标题</span></div>
<div class="modal-body"><img src="img/user-img.png"></div>
<div class="modal-footer"><a>确定</a></div>
</div>
</div>
补充样式如下:
.modal {
background-color: rgba(0, 0, 0, .6);
}
.model-body-main{
display: flex;
justify-content: center;
}
.modal-footer {
padding: 15px;
text-align: right;
/* position: fixed; */
border-top: 1px solid #e5e5e5;
display: flex;
justify-content: center;
}
.modal-footer a {
display: block;
height: 39px;
line-height: 38px;
padding: 0 18px;
background-color: var(--primary);
color: var(--white);
white-space: nowrap;
text-align: center;
font-size: 14px;
border: none;
width: 90px;
border-radius: 2px;
cursor: pointer;
margin-right: 5px;
}
.modal-footer a:hover {
text-decoration: none;
opacity: .8;
}
细心的同学已经发现了,这不是bootstrap的模态框吗?还真是,但是bootstrap的模态框的代码是写在html里面的,且是写在<dialog>标签内的,就会导致不会根据需求动态弹出想要的内容。因此我将其封装成一个弹框,但是样式是用bootstrap的样式。
先实现一个简单组装过程:js依赖于zq.js:在线访问
function openModelDialog1(config) {
var defaultConfig={
dialogWidth:600,//默认弹框宽为600px
dialogResizable:false,//默认弹框不能缩放
dialogTop:100,//默认距离顶部100px+xpx
dialogType:1,//默认弹框类型
time:false,//默认不自动关闭
head:{//默认头部配置
title:"标题",//默认头部标题
isDrag:false,//默认不能拖拽
closeFn:function(){//默认点击x前回调,在回调内return false将不会关闭弹框
}
},
body:{//默认身体
html:"",//默认内容
templet:"",//默认模板用[]占位
data:{},//模板的数据
},
footer:{//默认尾部
btn:[//默认按钮
// {
// btnText:"确定",
// btnFn:function(){
// zq.alert("点击了确定")
// }
// },
// {
// btnType:"cancel"
// }
]
}
};
var config=zq.zmergeEx(defaultConfig,config);//合并传入的配置项跟默认配置项,相同的配置项由传入的覆盖默认的
var model_index = $(".model").length;//获取当前页面有多少弹框
var model_class = "model_" + model_index;//生成唯一的class
var headConfig=config.head;//获取头部配置项
var bodyConfig=config.body;//获取身体配置项
var footerConfig=config.footer;//获取尾部配置项
var $model_close = $("<button></button>").addClass("close").append("<span>×</span>");//生成右上x的JQ对象
var $mode_title = $("<span></sapn>").addClass("modal-title").append(headConfig.title);//生成标题的JQ对象
var $model_header = $("<div></div>").addClass("modal-header");//生成弹框顶部JQ对象
$model_header.append($model_close).append($mode_title);//装配顶部
var $mode_body;//定义身体的JQ对象
if(bodyConfig.html==""){
//如果配置项的html参数为空(注意由于html是默认配置项的内容,在合并的时候要么有值,要么是默认的""),就加载模板内容~~之后会将
$mode_body = $("<div></div>").addClass("modal-body model-body-main").append(zq.loadTemplete(bodyConfig.templete,bodyConfig.data));
}else{
//如果配置项html参数不为空,将html填充至弹框身体内
$mode_body = $("<div></div>").addClass("modal-body model-body-main").append(bodyConfig.html);
}
var $mode_footer;//定义尾部的JQ对象
if(footerConfig.btn.length==0){
//如果用户没有配置,将尾部JQ对象置为空,这样就装配不了尾部了
$mode_footer="";
}else{
//如果用户配置了尾部
var btnConfig=footerConfig.btn;//获取按钮的配置项
$mode_footer = $("<div></div>").addClass("modal-footer");//生成尾部的JQ对象
//遍历按钮配置项,装配按钮至尾部JQ对象
$.each(btnConfig,function(index,value){
if(zq.isUndefined(value.btnType)){
//配置项无btnType,用配置项的配置
}else if(value.btnType=="cancel"){//配置项有btnType,用预设的配置
value={
btnText:"取消",
btnFn:function(){
$model.remove();
}
}
}
//装配按钮
$("<a>"+value.btnText+"</a>").on("click", function() {
value.btnFn();
}).appendTo($mode_footer);
})
}
var $model = $("<div></div>").addClass("modal");//生成最外层JQ对象,这个承载了整个弹框的DOM的JQ对象包括遮罩
var $model_dialog = $("<div></div>").addClass("modal-dialog").width(config.dialogWidth).offset({top:config.dialogTop});//生成弹框外围元素的JQ对象,这个是用于弹框外围定位
var $model_content = $("<div></div>").addClass("modal-content");//生成弹框内部元素定位的JQ对象
$model_content.append($model_header).append($mode_body).append($mode_footer);//装配
$model.append($model_dialog.append($model_content))
.addClass(model_class).appendTo("body").show();//整个弹框及模态框装配进入body并展示出来
$model_close.on("click", function() {//右上x动态绑定回调
zq.timeout().then(function(){
if(headConfig.closeFn()===undefined||headConfig.closeFn()){//先执行回调函数后再关闭弹框,如果回调函数返回false将不会关闭弹框
return zq.timeout(200).then(function(){
$model.remove();
});
}else{
return false;
}
})
});
if(config.time&&zq.elementType(config.time)=="number"){//如果配置项有时间,则等待一段时间自动关闭弹框
setTimeout(function() {$model.remove()}, config.time);
}
if(headConfig.isDrag){//如果配置项(顶部)允许拖拽,将可以拖拽,基于JQueryUI
$model.children(".modal-dialog").draggable({ scroll: true,cursor: "move",handle: ".modal-header" });
}
if(config.dialogResizable){//如果配置项允许缩放,则可以缩放,基于JQueryUI
$model.children(".modal-dialog").resizable({minHeight: 150,minWidth: 200,});
}
//弹框成功将返回一个自定义对象给调用方,方便调用方根据回调内容做一些操作,如通过对象的model_class去关闭弹框
return {
"component_type":"model",
"model_class":model_class
};
}
/**
* 内部方法:关闭弹框
* @param {Object} index
*/
function closeModel(index){
$("." + index).remove();
}
最后是调用:
var classIndex=openModelDialog1(
{
dialogResizable:true,//允许缩放
head:{
title:"zqcy",//标题是zqcy
isDrag:true,//允许拖拽
closeFn:function(){
alert("点击lx");
//return false;
}
},
body:{
html:"<img src='img/user-img.png'>hoppinzq.com/video/view"//内容是一张图片
},
footer:{
btn:[
{
btnText:"确定",
btnFn:function(){//点击确定回调函数
zq.alert("点击了确定");
//在回调里closeModel(classIndex.model_class)关闭弹框
}
},
{
btnType:"cancel"
}
]
}
});
console.log("该组件的返回值,可手动掉用closeModel(classIndex.model_class)关闭该弹框:"+classIndex);
效果拔群:
最后将拓展一些预定样式即可。下篇将我第一个组件拓展进我们自己的js里
在线访问链接:链接
源代码下载:链接
未完待续。。。。