JS:原生JS实现message消息提示框

简介

学习了一下如何使用原生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'
}

// 创建DOM
const 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'
      break
    case MessageType.ERROR:
      bgColor = 'background-color: #f0f9eb'
      break
    case MessageType.WARNING:
      bgColor = 'background-color: #f0f9eb'
      break
    default:
      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};`
      break
    case MessageType.ERROR:
      cssStr = commonCss + `${getTypeBGColor(type)};color: ${MessageTypeColor.ERROR};`
      break
    case MessageType.WARNING:
      cssStr = commonCss + `${getTypeBGColor(type)};color: ${MessageTypeColor.WARNING};`
      break
    default:
      cssStr = commonCss + `${getTypeBGColor(type)};color: ${MessageTypeColor.MESSAGE};`
      break
  }
  return cssStr
}

// 获取类型对应的icon图标的code
const getIconClass = type => {
  let iconClass = ''
  switch (type) {
    case MessageType.SUCCESS:
      iconClass = MessageIcons.SUCCESS
      break
    case MessageType.ERROR:
      iconClass = MessageIcons.ERROR
      break
    case MessageType.WARNING:
      iconClass = MessageIcons.WARNING
      break
    default:
      iconClass = MessageIcons.MESSAGE
      break
  }
  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};`
      break
    case MessageType.ERROR:
      cssStr = commonCss + `color: ${MessageTypeColor.ERROR};`
      break
    case MessageType.WARNING:
      cssStr = commonCss + `color: ${MessageTypeColor.WARNING};`
      break
    default:
      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 warning
    content: 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 = messageContainer
  config.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 = true
    if (isClick) {
      mainContainer.removeChild(messageContainer)
      _resetMianPosition(mainContainer)
      free()
    } else {
      setTimeout(() => {
        mainContainer.removeChild(messageContainer)
        _resetMianPosition(mainContainer)
        free()
      }, 400)
    }
  }

  // 销毁重置绑定
  function free() {
    config.els = null
    config.elsAgainBtn = null
    config.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)
  }
  // 鼠标移出: 一秒后销毁当前message
  domConfig.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自己写一个类似的消息提示框:此处是连接(简易版)

最后

觉得有用的朋友请用你的金手指点一下赞,或者评论留言一起探讨技术!

  • 11
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
JS原生实现穿梭框可以使用HTML的select标签和一些JavaScript代码实现。以下是一个基本的穿梭框代码示例: ```html <!DOCTYPE html> <html> <head> <title>JS穿梭框</title> <meta charset="utf-8"> </head> <body> <label>选择:</label> <select id="select1" multiple="multiple" style="width:200px;height:200px;"> <option value="1">选项1</option> <option value="2">选项2</option> <option value="3">选项3</option> <option value="4">选项4</option> <option value="5">选项5</option> </select> <button onclick="moveLeft()">←</button> <button onclick="moveRight()">→</button> <select id="select2" multiple="multiple" style="width:200px;height:200px;"> </select> <script type="text/javascript"> function moveLeft() { var select1 = document.getElementById("select1"); var select2 = document.getElementById("select2"); for (var i = 0; i < select2.options.length; i++) { if (select2.options[i].selected) { select1.appendChild(select2.options[i]); i--; } } } function moveRight() { var select1 = document.getElementById("select1"); var select2 = document.getElementById("select2"); for (var i = 0; i < select1.options.length; i++) { if (select1.options[i].selected) { select2.appendChild(select1.options[i]); i--; } } } </script> </body> </html> ``` 在这个代码中,我们使用了两个select标签,一个是源选项,另一个是目标选项。通过点击左右移动按钮,我们可以将一个或多个选项从一个select标签移动到另一个select标签中。这个代码只是一个基本的示例,你可以根据自己的需要进行修改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值