先看效果
由于每次修改element的弹窗样式颜色觉得麻烦,于是就想着自己搞一个,想怎么玩就怎么玩,开干,走起
- 1、首先创建div
- 2、给div添加内容
- 3、把div放入页面中显示
- 4、添加样式
- 5、添加事件
- 6、完成移除添加的div
一、如何创建div,使用document.createElement
const div = document.createElement("div");
div.innerHTML = `<div >测试</div>`
二、创建完后怎么把div加入到页面中,所谓的页面是什么,要么是document.body,要么是你想让他显示到某个元素的中
这里我就直接显示在body中去,首先得判断dom元素的position是否是relative,这里用到了window中的getComputedStyle来进行判断
let container = document.body
if (getComputedStyle(container).position === 'static') {
container.style.position = "relative";
}
//将div加入到容器中
container.appendChild(div);
三、样式添加 由于使用的是CSS Modules,不是正常的使用,css文件相当于一个对象,导出css文件,然后在去获取
import messageBox from "./messageBox.module.css"
div.innerHTML = `<div class="${messageBox.messageBoxTip}">测试</div>
<div class="${messageBox.messageBoxTitle}">
测试
</div>`
div.className = `${messageBox.messageBoxContainer}`;
这里得注意,如果使用了css3想要添加动画比如给div的opacity从0到1的过渡,
如果你直接加它是不会有过渡的,由于浏览器渲染是异步的,他不可能执行到上面就直接渲染了,他要等我们代码执行完后才渲染,所以最终显示的opacity的值就是下面设置的1
如果想要有动画效果,就得强制渲染,你只要读取该元素的位置,尺寸等就会导致重新渲染(reflow重排)
在添加到容器中后立马要强制渲染 div.clientHeight;
四:如果有按钮进行点击事件直接使用id,getElementById来获取该元素
<div class="${messageBox.messageBoxBtn}">
<div id="cancelBox" class="${messageBox.boxBtn}">${options.cancelButtonText}</div>
<div id="confirmBox" class="${messageBox.boxBtn}">${options.confirmButtonText}</div>
</div>
cancelBtn.onclick = () => {}
confirmBtn.onclick = () => {}
五:事情做完了就得把div移除,不然还会在中间显示只是看不到而已
div.remove();
补充:添加组件
如果想要在div中加入图标,你可以使用图片,但如果你之前已经写好了一个图标组件,那该怎么把这个组件添加进去呢?
假设有个图标组件:导入组件 import Icon from '/components/Icon'
1、首先html中不可能直接使用组件Icon,我们得把组件转换从html元素
2、如何转换,这里我们就得使用vue 的render函数 在使用前得先导入vue
import Vue from "vue";
function getComponentRootDom(comp, props) {
const vm = new Vue({
render: h => h(comp, {props})
})
vm.$mount();
return vm.$el;
}
参数 1、comp 需传入组件,
2、props 组件需要的参数
返回 最终返回的是该组件渲染后的dom树
3、然后就可以在div中加入,然后就可以显示图标了然后在给div添加样式就行了
const icon=getComponentRootDom(Icon,type)
div.innerHTML = `<div>${icon}</div>`
完整代码 在js中返回一个promise是为了点击按钮有后可以续操作
文件目录
-
弹窗 messageBox.js
//这里引入图标组件,我这没有暂时不需要 // import Icon from '/components/Icon' import messageBox from "./messageBox.module.css" /** * 弹出框 * @param content 弹窗的内容显示 * @param tip 弹出的标题信息 * @param options 显示按钮的文字信息 * @returns {Promise<unknown>} * {HTMLElement} container 容器 消息会显示到该容器的正中间,如果不传,就直接显示页面正中间 */ export default function (content = '', tip = '提示', options = {confirmButtonText: '确认', cancelButtonText: '取消'}) { return new Promise((resolve, reject) => { //创建消息元素 const div = document.createElement("div"); div.innerHTML = `<div class="${messageBox.messageBoxTip}">${tip}</div> <div class="${messageBox.messageBoxTitle}"> ${content} </div> <div class="${messageBox.messageBoxBtn}"> <div id="cancelBox" class="${messageBox.boxBtn}">${options.cancelButtonText}</div> <div id="confirmBox" class="${messageBox.boxBtn}">${options.confirmButtonText}</div> </div>` //设置div样式 div.className = `${messageBox.messageBoxContainer}`; //默认在页面中间,想要自定义可以作为参数传入container let container = document.body if (getComputedStyle(container).position === 'static') { container.style.position = "relative"; } //将div加入到容器中 container.appendChild(div); //由于浏览器渲染是异步的,他不可能执行到上面就直接渲染了,他要等我们代码执行完后才渲染,所以最终显示的opacity的值就是下面设置的1 // 如果想要有动画效果,就得强制渲染,你只要读取该元素的位置,尺寸等就会导致重新渲染(reflow重排) div.clientHeight; //回归到正常位置 div.style.opacity = 1; div.style.transform = `translate(-50%,-50%)`; const cancelBtn = document.getElementById('cancelBox'); const confirmBtn = document.getElementById('confirmBox'); //取消按钮的点击事件 cancelBtn.onclick = () => { div.style.opacity = 0; div.style.transform = `translate(-50%,-50%) translateY(-25px)`; //虽然消失了但是元素还在,还得把元素移除,监听动画移除事件(该事件只触发一次) div.addEventListener('transitionend', () => { div.remove(); reject(); }, {once: true}) } //确认按钮的点击事件 confirmBtn.onclick = () => { div.style.opacity = 0; div.style.transform = `translate(-50%,-50%) translateY(-25px)`; //虽然消失了但是元素还在,还得把元素移除,监听动画移除事件(该事件只触发一次) div.addEventListener('transitionend', () => { div.remove(); resolve(); }, {once: true}) } }) } /** * 获取某个组件渲染的dom * @param comp 组件 * @param props 组件需要的参数 * @returns {Element} */ import Vue from "vue"; function getComponentRootDom(comp, props) { const vm = new Vue({ render: h => h(comp, {props}) }) vm.$mount(); return vm.$el; }
-
弹窗的css messageBox.module.css
/*大盒子样式*/ .messageBoxContainer { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); z-index: 9999; box-shadow: -2px 2px 5px rgba(0, 0, 0, 0.5); padding: 10px 20px; width: 300px; height: 150px; display: flex; flex-direction: column; align-items: center; justify-content: space-around; border-radius: 8px; color: #000; transition: 0.4s; transform: translate(-50%, -50%) translateY(25px); white-space: nowrap; background: #fff; } .messageBoxTip { width: 100%; height: 20px; line-height: 20px; text-align: left; } .messageBoxTitle { width: 100%; height: 30px; line-height: 30px; padding-left: 4%; box-sizing: border-box; } .messageBoxBtn { width: 100%; display: flex; justify-content: flex-end; align-items: center; } .boxBtn { text-align: center; position: relative; width: 50px; height: 30px; line-height: 30px; font-size: 16px; margin: 0 4%; cursor: pointer; } .boxBtn:hover { background: #21ebff; color: #111; transition-delay: 0.5s; } .boxBtn:nth-child(1) { filter: hue-rotate(270deg); } .boxBtn:nth-child(2) { filter: hue-rotate(90deg); } .boxBtn::before { content: ''; position: absolute; top: 0; left: 0; width: 10px; height: 10px; border-top: 2px solid #21ebff; border-left: 2px solid #21ebff; transition: 0.5s; transition-delay: 0.5s; } .boxBtn:hover::before { width: 100%; height: 100%; transition-delay: 0s; } .boxBtn::after { content: ''; position: absolute; right: 0; bottom: 0; width: 10px; height: 10px; border-bottom: 2px solid #21ebff; border-right: 2px solid #21ebff; transition: 0.5s; transition-delay: 0.5s; } .boxBtn:hover::after { width: 100%; height: 100%; transition-delay: 0s; }
-
消息js myMessage.js
//这里引入图标组件,我这没有暂时不需要 // import Icon from '/components/Icon' import messageStyle from "./myMessage.module.css" /** * 弹出消息 * {string} content 消息内容 * {string} type 消息类型 info danger success, warning * {Number} duration 多久消失, 0为不消失 * {HTMLElement} container 容器 消息会显示到该容器的正中间,如果不传,就直接显示页面正中间 */ export default function (content='成功',options = {}) { const type = options.type || 'success'; const duration = options.duration || 2000; const container = options.container || document.body; //创建消息元素 const div = document.createElement("div"); //如果想要放图标得导入图标组件,必须先得到图标的dom元素,这里传入type类型显示不同的图标 let Icon = null;//由于我这里没有这个图标组件就不显示了 if (Icon) { const iconDom = getComponentRootDom(Icon, {type}); // div.innerHTML=`<span>这里如果想要放图标dom</span><div>这里显示你要展示的内容</div>` div.innerHTML = `<span>${iconDom.outerHTML}</span><div>${content}</div>` } else { div.innerHTML = `<span>${content}</span>` } //设置div样式 div.className = `${messageStyle.myMessage} ${messageStyle["myMessage-" + type]}`; //将div加入到容器中 //容器的position是否改动过 if (getComputedStyle(container).position === 'static') { container.style.position = "relative"; } container.appendChild(div); //由于浏览器渲染是异步的,他不可能执行到上面就直接渲染了,他要等我们代码执行完后才渲染,所以最终显示的opacity的值就是下面设置的1 // 如果想要有动画效果,就得强制渲染,你只要读取该元素的位置,尺寸等就会导致重新渲染(reflow重排) div.clientHeight; //回归到正常位置 div.style.opacity = 1; div.style.transform = `translate(-50%,-50%)`; //等一段时间消失 setTimeout(() => { div.style.opacity = 0; div.style.transform = `translate(-50%,-50%) translateY(-25px)`; //虽然消失了但是元素还在,还得把元素移除,监听动画移除事件(该事件只触发一次) div.addEventListener('transitionend', () => { div.remove(); //运行回调函数 消失之后去做一些事情 options.callback && options.callback() }, {once: true}) }, duration) } /** * 获取某个组件渲染的dom * @param comp * @param props * @returns {Element} */ import Vue from "vue"; function getComponentRootDom(comp, props) { const vm = new Vue({ render: h => h(comp, {props}) }) vm.$mount(); return vm.$el; }
-
消息的css myMessage.module.css
.myMessage { position: absolute; left: 50%; top: 6%; transform: translate(-50%, -50%); z-index: 9999; box-shadow: -2px 2px 5px rgba(0, 0, 0, 0.5); padding: 10px 20px; line-height: 2; display: flex; min-width: 100px; align-items: center; justify-content: center; border-radius: 8px; color: #fff; transition: 0.4s; transform: translate(-50%, -50%) translateY(25px); white-space: nowrap; } /*默认消息颜色*/ .myMessage-info { background: #909399; } /*成功消息颜色*/ .myMessage-success { background: #67C23A } /*警告消息颜色*/ .myMessage-warn { background: #E6A23C; } /*危险消息颜色*/ .myMessage-danger { background: #F56C6C; }
到这里代码已经完结,那么怎么用呢,测试的话可以在vue项目的main.js中导入使用就行了
import myMessage from "./utils/myMessage/myMessage";
import myMessageBox from "./utils/messageBox/messageBox";
myMessageBox('是否进行删除').then(()=>{
console.log("确认");
myMessage('删除成功')
}).catch(()=>{
myMessage('已取消')
console.log('取消')
});
如果想要在整个vue实例中使用这个方法就可以把方法挂到vue实例中去,然后用this.$XX就可以了
import myMessage from "./utils/myMessage/myMessage";
import myMessageBox from "./utils/messageBox/messageBox";
Vue.prototype.$myMessage=myMessage;
Vue.prototype.$myMessageBox=myMessageBox;
//在vue 页面中使用
this.$myMessageBox('是否进行删除').then(()=>{
console.log("确认");
this.$myMessage('删除成功')
}).catch(()=>{
this.$myMessage('已取消')
console.log('取消')
});
在这里提示一下:在vue的prototype原型上加属性和方法都会默认在名字前面添加$符号,在vue的原型上添加属性或方法后就可以在项目的任何组件中使用这些方法和属性,
那为什么要加$符号呢,比如:你在原型中定一个方法叫say,但是没有加$符号去区分,而在组件的data中也定义了一个相同的say的数据,
当在这个页面去使用的时候,你本来是想调用你在原型上定义的方法,结果打印this.say发现是data里面的值而不是方法,
所以这就是为什么最好要加上$的原因,为了区分防止被覆盖了
感谢各位大佬的观看,如有问题请大佬指点