纯js手写一个element的弹窗,方便修改自己想要的样式

21 篇文章 0 订阅
本文介绍了如何在Vue项目中创建一个可自定义的弹窗组件,包括内容、样式、事件处理和动画效果,并提供了一个简单的消息提示组件。通过动态创建DOM元素,设置CSS样式,监听事件并移除元素,实现了弹窗的显示和隐藏。此外,还讨论了浏览器渲染的异步性质以及强制渲染的技巧。文章最后展示了如何在Vue实例中挂载和使用这两个组件。
摘要由CSDN通过智能技术生成

先看效果

由于每次修改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里面的值而不是方法,

所以这就是为什么最好要加上$的原因,为了区分防止被覆盖了

感谢各位大佬的观看,如有问题请大佬指点

 

好的,我可以帮你手写一个基于Vue3的弹窗拖拽组件示例代码。 首先,你需要安装Vue3: ``` npm install vue@next ``` 然后,创建一个Draggable.vue组件,代码如下: ```vue <template> <div class="draggable" :style="{ left: left + 'px', top: top + 'px' }"> <div class="title" @mousedown="dragStart"> {{ title }} </div> <div class="content"> <slot /> </div> </div> </template> <script> import { ref } from 'vue'; export default { props: { title: { type: String, default: '', }, }, setup(props, { emit }) { const left = ref(0); const top = ref(0); let startX, startY, offsetX, offsetY; function dragStart(e) { startX = e.clientX; startY = e.clientY; offsetX = left.value; offsetY = top.value; document.addEventListener('mousemove', drag); document.addEventListener('mouseup', dragEnd); } function drag(e) { const moveX = e.clientX - startX; const moveY = e.clientY - startY; left.value = offsetX + moveX; top.value = offsetY + moveY; } function dragEnd() { document.removeEventListener('mousemove', drag); document.removeEventListener('mouseup', dragEnd); } return { left, top, dragStart, }; }, }; </script> <style scoped> .draggable { position: absolute; width: 300px; height: 200px; background-color: #fff; border: 1px solid #ccc; box-shadow: 1px 1px 5px #ccc; } .title { cursor: move; padding: 10px; background-color: #eee; border-bottom: 1px solid #ccc; } .content { padding: 10px; } </style> ``` 在上面的代码中,我们使用了Vue3的Composition API来实现拖拽功能,使用了ref来定义left和top变量,使用了setup函数来定义组件逻辑。在dragStart函数中,我们记录了鼠标按下时的坐标和弹窗的偏移量,在drag函数中计算出弹窗的新位置,最后在dragEnd函数中移除了鼠标移动事件。 使用这个组件非常简单,只需要在父组件中引入Draggable组件,然后使用类似于普通的HTML标签的方式来使用它,例如: ```vue <template> <div> <button @click="showDialog = true">打开弹窗</button> <draggable v-if="showDialog" title="弹窗标题"> <p>这是弹窗内容</p> <button @click="showDialog = false">关闭弹窗</button> </draggable> </div> </template> <script> import Draggable from './Draggable.vue'; export default { components: { Draggable, }, data() { return { showDialog: false, }; }, }; </script> ``` 在上面的代码中,我们在父组件中引入了Draggable组件,并在父组件的data对象中定义了一个showDialog变量来控制弹窗的显示和隐藏。当点击打开弹窗按钮时,showDialog变量会被设置为true,从而显示弹窗;当点击弹窗内的关闭按钮时,showDialog变量会被设置为false,从而隐藏弹窗。 希望这个示例可以帮助你手写一个基于Vue3的弹窗拖拽组件!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值