前言
毋庸置疑,现在的CP经理对于浏览器自带的弹出框已经产生审美疲劳。
在这里为什么我会强调CP经理呢?因为下面的陈述句可能就是你我都日常:
…我认为这个需求可以实现
…哦?这个效果实现不了吗?
…那个太难看了,咱能不能自己做个?
…现在我就是用户!!!
…
注:CP经理——传说中一种可怕的怪物,对于
程序猿
有望而生畏的核弹级杀伤力!
是故,
为了守护世界的和平!
为了防止世界被破坏!!
为了不再996!!!
为了面前的诸位小可爱们不再承受可怕的定制化弹出框
的迫害!
我!一名茫茫苦海屌丝程序员!决定亲自手把手教你精准、快速、高效的完成属于自己的、个性化的弹出框设计方案!
目录
效果图
仿prompt
仿alert
仿confirm
基本样式
源码地址
Github地址: https://github.com/ming131419/pxu
JS源码地址:https://github.com/ming131419/pxu/blob/dev/index.js
压缩JS地址:https://github.com/ming131419/pxu/blob/dev/dist/index.js
项目主要文件释义:
pxu:
- index.html // 示例html
- index.js // 主文件,闭包封装pxu(Personal Window)
由于master
分支主要提供npm
组件包,所以如果您想拥有属于自己的可定制化的pxu
,我建议您在dev
上clone/fork
哦!
操作步骤
第一步、引入依赖
目前提供两种方式引入。
- 通过
npm install pxu
安装 - 通过下载/复制
js
导入,可通过上面github链接进行复制
注意:
如果您的项目是Node
并且使用Vue
等框架,建议您通过npm install pxu
方式安装;
如果您的项目以原生JS
开发为主,建议您直接复制链接中JS
内容进行使用
如果您的项目既使用了Vue
等框架,并且也有原生JS开发部分,那也可以直接引入JS
文件使用呦!
其实我之所以要写这个插件,主要还是为了解决我现在所在项目中所遇到的问题——我们项目分为两部分,一部分作为后端和官网首页,这一部分的页面按主要使用Nunjucks组件模板;另一部分主要作为后台管理,使用Vue组件模板。
第二步、使用方式
// pxu($1, $2, $3, $4, $5)
// $1 : 表示弹出框的icon图标类型,目前仅提供warning\success\shutdown\restart四种icon
// $2 : 表示弹出框的的标题,可以是html字符串
// $3 : 表示弹窗的主要内容描述对象,可以是html字符串。
// 注意:如果html字符串中包含input,那么该标签中必须要包含id为pxu的属性,否则关闭弹出框获取不到确认数据,
// 示例:<input id="pxu" type="text" placeholder="请输入登录密码" style="..." />
// $4 : 表示弹出框的取消按钮
// $5 : 表示弹出框的确认按钮,必填项,默认为“确定”
pxu('warning', // 可为null
'您确认继续重启吗?', // 可为null
'请确保您的信息填写正确后,提交后无法修改', // 可为null
'取消', // 可为null
'立即前往').then(res => {
console.log(res)
});
仿prompt样式代码:
pxu(null,
'安全校验',
'<input id="pxu" type="text" placeholder="请输入登录密码" />',
'取消',
'确定').then(res => {
console.log(res)
});
仿alert样式代码:
pxu('warning',
null,
'Please make sure that your information is filled in correctly and cannot be modified after submission?',
null,
'确定').then(res => {
console.log(res)
});
仿confirm样式代码:
pxu('success',
null,
'您的操作存在风险,是否继续?',
'取消',
'确定').then(res => {
console.log(res)
});
基本样式样式代码:
pxu('restart',
'是否重启',
'您的操作存在风险,是否继续?',
'取消',
'确定').then(res => {
console.log(res)
});
源码分析(高级)
该插件采用常见的闭包方式进行分装,对外仅爆露pxu
函数
window.pxu = (function (win, doc) {
...
return async function (type, title, des, btnCancel, btnEnsure) {
type = type || ''; // window type. At present, only support warning\success\shutdown\restart
title = title || ''; // pop title
des = des || ''; // confirm info
btnCancel = btnCancel || ''; // cancel button info
btnEnsure = btnEnsure || '确定'; // ensure button info
return await personalWindow({
type: type,
title: title,
des: des,
btnCancel: btnCancel,
btnEnsure: btnEnsure
})
}
})(window, document);
出于练习目的,模仿jq
封装了下$
函数,代码实现也很简单,通过querySelector
实现:
let $ = function (selector) {
// let t = selector.trim().substring(0, 1);
// let el = selector.trim().substring(1, Infinity);
// let type = {
// '.': function (el) {
// return doc.getElementsByClassName(el);
// },
// '#': function (el) {
// return doc.getElementById(el);
// }
// };
return doc.querySelector(selector) || null
};
为了方便后续扩展,通过JSON
对象方式封装了html
标签的创建,要注意的是_html$
对象中的顺序关乎html
中标签创建的顺序,不建议随便更改。对象中key
表示标签的唯一标识,后者表示要创建的标签类型:
self._html$ = {
popLayer: 'div',
popBox: {
closeIcon: 'div',
type: 'div',
title: 'div',
des: 'div',
btnBox: {
btnCancel: 'button',
btnEnsure: 'button'
}
}
};
self._box$ = personalWindow.createBox(self._html$, doc.getElementsByTagName("body")[0]);
personalWindow.createBox = function (html, parent) {
let res = {};
let deepCreate = function (obj, parent) {
if (!obj) return;
for (let _k in obj) {
if (typeof obj[_k] === "object") {
res[_k] = personalWindow.createTag(parent, 'div', personalWindow.camel2underline(_k))
deepCreate(obj[_k], res[_k]);
} else {
res[_k] = personalWindow.createTag(parent, obj[_k], personalWindow.camel2underline(_k));
}
}
return res
};
return deepCreate(html, parent);
};
插件中的css
样式也是通过JSON
对象封装处理的:
personalWindow.existOf(self, 'btnCancel', {
"padding": "6px 41px",
"border": "1px solid var(--btn-color)",
"border-radius": "5px",
"cursor": "pointer",
"outline": "none",
"margin-right": "30px",
"background-color": "var(--default-bg-color)",
"flex": "auto"
}); // CSS 对象通过封装的this.obj2css
personalWindow.existOf = function (self, item, style, obj) {
let option = self._options$,
box = self._box$;
console.log(`box[item]: `, box[item]);
if (item && option[item]) {
box[item].innerHTML = obj || option[item];
this.appendStyle({
[box[item]['class']]: style
})
} else {
box[item].remove();
}
};
personalWindow.appendStyle = function (style) {
this._style$.innerText += this.obj2css(style)
};
// 只是通过简单的字符替换进行转化的
personalWindow.obj2css = function (obj) {
return JSON.stringify(obj).trim()
.replace(/^\{|\"|\}$/g, '')
.replace(/\,/g, ';')
.replace(/\}/g, ';}')
.replace(/\:\{/g, '{')
.replace(/\}\;/g, '}')
.replace(/DOT/gi, ',');
};
除以上比较核心的几个点外,插件里面还分装了需要用的方法,比如将驼峰命名变量转化成下划线命名等。
其他的话
1、插件里面icon
主要使用svg
格式,如果您想定制属于的自己的icon
图标,我建议您也通过这种方式哦!
图片转换可参考:jpg,png图片在线转svg
2、要注意的是插件里面的css
样式采用的是flex
布局,如果您欠缺这方面的知识,可参考阮一峰老师的Flex 布局教程:语法篇
3、目前,该插件不支持动画弹出效果,至于要不要做这个效果,还要看小伙伴在评论区留言呀!如果您对该插件有更好的意见或建议欢迎在github
上提issure
或者评论区留言!
4、如果本篇恰好get到了你的点,而您恰好又对私人定制化更加感兴趣——想要做出适用于自己项目所需要的弹出框及样式,那么我会详写一篇关于pxu
二次开发的文章呦!
5、小博主正式接触前端不到半年,大佬们看到源码可能感觉比较low
,有改进的地方希望大佬能够指出!
TIP
这篇文章完成于1024的前一天,本来打算1024发布的,但出于npm插件管理不熟悉的缘故导致拖延到今天!如果大家感兴趣的话,我也会写一篇关于在npm
上发布组件的文章呢!