简介
学习了一下如何使用原生JS(加上class类)实现message消息提示框的封装(繁琐版)。
使用到的主要技术点:class类,JS。
功能点
1.四种提示状态(message success error warning)及文字显示;
2.不再提示(相同文字内容的消息)的功能按钮;
3.鼠标移入不消失,移出一秒后消失;
具体实现
1.新建一个message.js文件
// 消息类型const MessageType = {MESSAGE: 'message', // 普通SUCCESS: 'success', // 成功ERROR: 'error', // 错误WARNING: 'warning' // 警告}// 状态icon图标const MessageIcons = {MESSAGE: 'el-icon-info',SUCCESS: 'el-icon-success',ERROR: 'el-icon-error',WARNING: 'el-icon-warning'}// 状态对应的主色const MessageTypeColor = {MESSAGE: '#909399',SUCCESS: '#67c23a',ERROR: '#f56c6c',WARNING: '#e6a23c'}// 创建DOMconst createDom = ({isId = false, name = '', tag = 'div' }) => {if (!tag) {return null}const ele = document.createElement(tag)if (name) {if (isId) {ele.id = name} else {ele.className = name}}return ele}// 获取类型对应的背景色const getTypeBGColor = type => {let bgColor = ''switch (type) {case MessageType.SUCCESS:bgColor = 'background-color: #f0f9eb'breakcase MessageType.ERROR:bgColor = 'background-color: #f0f9eb'breakcase MessageType.WARNING:bgColor = 'background-color: #f0f9eb'breakdefault:bgColor = 'background-color: #edf2fc'break}return bgColor}// 获取类型对应的背景色、文字颜色const getTypeDomCss = type => {let cssStr = ''let commonCss = ''switch (type) {case MessageType.SUCCESS:cssStr = commonCss + `${getTypeBGColor(type)};color: ${MessageTypeColor.SUCCESS};`breakcase MessageType.ERROR:cssStr = commonCss + `${getTypeBGColor(type)};color: ${MessageTypeColor.ERROR};`breakcase MessageType.WARNING:cssStr = commonCss + `${getTypeBGColor(type)};color: ${MessageTypeColor.WARNING};`breakdefault:cssStr = commonCss + `${getTypeBGColor(type)};color: ${MessageTypeColor.MESSAGE};`break}return cssStr}// 获取类型对应的icon图标的codeconst getIconClass = type => {let iconClass = ''switch (type) {case MessageType.SUCCESS:iconClass = MessageIcons.SUCCESSbreakcase MessageType.ERROR:iconClass = MessageIcons.ERRORbreakcase MessageType.WARNING:iconClass = MessageIcons.WARNINGbreakdefault:iconClass = MessageIcons.MESSAGEbreak}return iconClass}// 获取类型对应的icon图标的额外样式const getTypeIconCss = type => {let cssStr = ''let commonCss = 'margin-right: 10px;font-size: 20px;'switch (type) {case MessageType.SUCCESS:cssStr = commonCss + `color: ${MessageTypeColor.SUCCESS};`breakcase MessageType.ERROR:cssStr = commonCss + `color: ${MessageTypeColor.ERROR};`breakcase MessageType.WARNING:cssStr = commonCss + `color: ${MessageTypeColor.WARNING};`breakdefault:cssStr = commonCss + `color: ${MessageTypeColor.MESSAGE};`break}return cssStr}const createMessage = ({type, content, duration, delay, againBtn, minWidth, maxWidth }, mainContainer) => {if (!mainContainer) {console.error('主容器不存在,查看调用流程,确保doucument.body已生成!')return}/**随机的key */const randomKey = Math.floor(Math.random() * (99999 - 10002)) + 10002/**属性配置 */const config = {isRemove: false, // 是否被移除了type: type || MessageType.MESSAGE, // 类型 message success error warningcontent: content || '', // 提示内容duration: duration || 3000, // 显示时间delay: delay || 0, // 弹出延迟timeout: null, // 计时器事件againBtn: againBtn || false // 是否需要显示 不再提示 按钮}// #region 生成DOM、样式、关系const messageContainer = createDom({name: `message-${randomKey}`, tag: 'div' })messageContainer.style = `min-width: ${minWidth}px;max-width:${maxWidth}px;padding: 12px 12px;margin-top: -20px;border-radius: 4px;box-shadow: -5px 5px 12px 0 rgba(204, 204, 204, 0.8);${getTypeBGColor(config.type)};animation: all cubic-bezier(0.18, 0.89, 0.32, 1.28) 0.4s;transition: all .4s;pointer-events: auto;overflow:hidden;`/**内容区域 */const messageTypeDom = createDom({tag: 'div' })messageTypeDom.style = getTypeDomCss(config.type)// icon// const messageTypeIcon = createDom({ name: `icon iconfont ${getIconClass(config.type)}`, tag: 'i' })const messageTypeIcon = createDom({name: `${getIconClass(config.type)}`, tag: 'i' })messageTypeIcon.style = getTypeIconCss(config.type)/**文本内容 */const messageTypeText = createDom({tag: 'span' })messageTypeText.style = 'font-size: 14px;line-height: 20px;'messageTypeText.innerHTML = config.content/**建立html树关系 */messageTypeDom.appendChild(messageTypeIcon)messageTypeDom.appendChild(messageTypeText)messageContainer.appendChild(messageTypeDom)/**不再提示的按钮 */if (config.againBtn) {const messageAgainDiv = createDom({name: 'message-again-btn', tag: 'div' })messageAgainDiv.style = `margin-top: 5px;text-align: right;`const messageAgainBtnText = createDom({name: 'message-again-text', tag: 'span' })messageAgainBtnText.innerHTML = '不再提示'messageAgainBtnText.style = `font-size: 12px;color: rgb(204, 201, 201);border-bottom: 1px solid rgb(204, 201, 201);cursor: pointer;`// 鼠标移入messageAgainBtnText.onmouseover = () => {messageAgainBtnText.style.color = '#fdb906'messageAgainBtnText.style.borderBottom = '1px solid #fdb906'}// 鼠标移出messageAgainBtnText.onmouseout = () => {messageAgainBtnText.style.color = 'rgb(204, 201, 201)'messageAgainBtnText.style.borderBottom = '1px solid rgb(204, 201, 201)'}messageAgainDiv.appendChild(messageAgainBtnText)messageContainer.appendChild(messageAgainDiv)config.elsAgainBtn = messageAgainBtnText}mainContainer.appendChild(messageContainer)// #endregion/**绑定DOM、销毁事件,以便进行控制内容与状态 */config.els = messageContainerconfig.destory = destory.bind(this)function destory(mainContainer, isClick) {if (!config.els || !mainContainer || config.isRemove) {// 不存在,或已经移除,则不再继续return}config.els.style.marginTop = '-20px' // 为了过渡效果config.els.style.opacity = '0' // 为了过渡效果config.isRemove = trueif (isClick) {mainContainer.removeChild(messageContainer)_resetMianPosition(mainContainer)free()} else {setTimeout(() => {mainContainer.removeChild(messageContainer)_resetMianPosition(mainContainer)free()}, 400)}}// 销毁重置绑定function free() {config.els = nullconfig.elsAgainBtn = nullconfig.destory = null}return config}function _toBindEvents(domConfig, _self) {if (!domConfig) {return}// 不再提示按钮的事件绑定if (domConfig.againBtn && domConfig.elsAgainBtn) {// 鼠标点击:将内容记录下来,下次就不显示同内容的弹框domConfig.elsAgainBtn.onclick = () => {clearTimeout(domConfig.timeout)let sessionJson = sessionStorage.getItem('MESSAGE_DONT_REMIND_AGAIN')let tempArr = sessionJson ? JSON.parse(sessionJson) : []let dontRemindAgainList = Array.isArray(tempArr) ? tempArr : []dontRemindAgainList.push(domConfig.content)sessionStorage.setItem(_self.sessionStorageName, JSON.stringify(dontRemindAgainList))domConfig.destory(_self.mainContainer, true)}}// 鼠标移入:对销毁计时器进行销毁domConfig.els.onmouseover = () => {clearTimeout(domConfig.timeout)}// 鼠标移出: 一秒后销毁当前messagedomConfig.els.onmouseout = () => {domConfig.timeout = setTimeout(() => {domConfig.destory(_self.mainContainer)clearTimeout(domConfig.timeout)}, 1000)}// 延时隐藏domConfig.timeout = setTimeout(() => {domConfig.destory(_self.mainContainer)clearTimeout(domConfig.timeout)}, domConfig.duration)}function _resetMianPosition(mainContainer) {if (!mainContainer) {return}mainContainer.style.left = `calc(50vw - ${mainContainer.scrollWidth / 2}px)`}class messageControl {constructor() {this.minWidth = 380 // 内容显示宽度:最小值this.maxWidth = 800 // 内容显示宽度:最大值this.top = 45 // 整体的最顶部距离this.zIndex = 999 // 层级this.mainContainerIdName = 'selfDefine-message-box' // 主体DOM的id名this.sessionStorageName = 'MESSAGE_DONT_REMIND_AGAIN' // 存储session信息的key/**生成主体DOM、样式容器 */let mainDom = document.getElementById(this.mainContainerIdName)if (mainDom) {document.body.removeChild(mainDom)}this.mainContainer = createDom({isId: true, name: this.mainContainerIdName, tag: 'div' })this.mainContainer.style = `pointer-events:none;position:fixed;top:${this.top}px;left:calc(50vw - ${this.minWidth / 2}px);z-index:${this.zIndex};display: flex;flex-direction: column;align-items:center;`document.body.appendChild(this.mainContainer)}/*** 消息提示* @param {String} type 类型 | 必传 | 可选值:message success error warning* @param {String} content 内容 | 必传 | ''* @param {Number} duration 显示时间 | 非必传 | 默认3000毫秒* @param {Number} delay 出现的延时 | 非必传 | 默认0* @param {Boolean} againBtn 是否显示 不再提示 按钮 | 非必传 | 默认false*/message(config = {}) {// 不再提示(相同文字内容)的存储与判断逻辑待优化let sessionJson = sessionStorage.getItem(this.sessionStorageName)let dontRemindAgainList = sessionJson ? JSON.parse(sessionJson) : null// 需要显示不再提示按钮,且内容有效,且不再提示的记录数组中包含本次内容,则不提示if (config.againBtn && config.content && dontRemindAgainList && Array.isArray(dontRemindAgainList) && dontRemindAgainList.includes(config.content)) {return}const domConfig = createMessage({type: config.type,content: config.content,duration: config.duration,delay: config.delay,againBtn: config.againBtn,minWidth: this.minWidth,maxWidth: this.maxWidth},this.mainContainer)this.mainContainer.appendChild(domConfig.els)domConfig.els.style.marginTop = '20px' // 为了过渡效果_resetMianPosition(this.mainContainer)_toBindEvents(domConfig, this)}beforeDestory() {if (this.mainContainer && this.mainContainer.remove) {this.mainContainer.remove()} else {document.body.removeChild(this.mainContainer)}this.mainContainer = null}}export {MessageType, messageControl }
2.在全局或者需要使用的地方引入并绑定
2.1局部使用
// 引入import {messageControl } from '@/views/components/messageDialog/messageDialog.js'// 初始化绑定this.messageComponent = new messageControl()// 调用this.messageComponent.message({content: '这里是内容' })
2.2全局使用
// 如Vue的main.js// 引入import {messageControl } from '@/views/components/messageDialog/messageDialog.js'// 初始化绑定const notifyCompoent = new messageControl()Vue.prototype.$selfMessage = notifyCompoent.message.bind(notifyCompoent)// 调用this.$selfMessage({content: '这里是内容' })
注意
icon使用时,注意className,尤其是图标的来源(此处是使用了element-ui框架,所以直接用的ui框架中的)。
结合vue自己写一个类似的消息提示框:此处是连接(简易版)
最后
觉得有用的朋友请用你的金手指点一下赞,或者评论留言一起探讨技术!