vue實(shí)現(xiàn)點(diǎn)擊出現(xiàn)操作彈出框的示例
如上圖所示,這次要實(shí)現(xiàn)一個(gè)點(diǎn)擊出現(xiàn)操作彈框的效果;并將這個(gè)功能封裝成一個(gè)函數(shù),便于在項(xiàng)目的多個(gè)地方使用。
具體思路是:
封裝一個(gè)組件,組件保護(hù)一個(gè)插槽,我們可以根據(jù)不同的場(chǎng)景,利用插槽隨意在這個(gè)彈框里插入任何元素,這個(gè)彈框顯示時(shí)根據(jù)我鼠標(biāo)的點(diǎn)擊位置,定位彈窗的位置,并在組件里面監(jiān)聽(tīng)鼠標(biāo)抬起事件,觸發(fā)事件時(shí)將彈窗隱藏;
接著在函數(shù)中利用createElement和appendChild方法將彈出框創(chuàng)建并插入到頁(yè)面中;
本次實(shí)現(xiàn)基于vuecli3
接下來(lái),具體實(shí)現(xiàn):
首先,我們先寫(xiě)一個(gè)demo組件
在點(diǎn)擊出現(xiàn)彈出框的元素上把事件對(duì)象數(shù)據(jù)傳遞一下,以便獲取點(diǎn)擊時(shí)鼠標(biāo)的數(shù)據(jù),以此確定彈出框的位置
// 文件路徑參考: src > views > demo> index.vue <template> <div class="demo-wrapper"> <div class="demo-div"> <span>更多功能</span> <i class="xk-icon xk-ellipsis" @click.stop='showMenu($event)'></i> // 為了獲取鼠標(biāo)位置,這里把事件對(duì)象數(shù)據(jù)傳遞一下 </div> </div> </template> <script lang="ts"> import { Vue, Component, Prop, Watch} from "vue-property-decorator"; @Component({ }) export default class articleView extends Vue { showMenu($event:any){ // 點(diǎn)擊時(shí)出現(xiàn)彈出框 } }; </script>
接著,我們把彈出框里面的組件也寫(xiě)一下
組件隨便命名為ActionList,組件里面把把列表數(shù)據(jù)及點(diǎn)擊事件都基于父組件傳遞的值而定,由于只是小demo,所以我們傳遞的menu數(shù)據(jù)數(shù)組只是簡(jiǎn)單的字符串?dāng)?shù)組
// 文件路徑參考: src > components > ActionList > index.vue<template> <ul class="menu-wrapper"> <li class="menu-item" v-for="item in menu" :key="item" @click="handleClick(item)" > {{ item }} </li> </ul> </template> <script lang="ts"> import { Component, Prop, Vue } from 'vue-property-decorator'; @Component export default class ActionList extends Vue { @Prop() menu: string[]; handleClick(str: string) { this.$emit('click', str); } } </script>
接著,開(kāi)始著手寫(xiě)彈框組件
1、彈框組件的顯示隱藏用v-show控制,為什么不用v-if ?因?yàn)檫@里我監(jiān)聽(tīng)了mouseup事件來(lái)讓彈框隱藏,如果在插槽里的元素綁定事件,比如點(diǎn)擊事件,用v-if 的話,點(diǎn)擊插槽里的元素時(shí),彈框先消失,插槽里的點(diǎn)擊事件就不會(huì)生效了。
2、handleOpen事件里我們根據(jù)鼠標(biāo)點(diǎn)擊位置定位彈框位置。
// 文件路徑參考: src > components > PublicModel > index.vue<template> <div class="dropdown-menu" :style="style" v-show='showModel'> <slot></slot> </div> </template> <script lang="ts"> import { Component, Prop, Vue } from 'vue-property-decorator'; interface IStyle { left?: string; right?: string; top?: string; bottom?: string; } @Component export default class PublicModel extends Vue { showModel:boolean = false; style:IStyle = {}; // 組件顯示時(shí) handleOpen($event:any){ const { clientWidth, clientHeight, scrollWidth, scrollHeight } = document.body || document.documentElement; const { pageX, pageY, clientX, clientY } = $event; let style:IStyle = {} if(clientX > (clientWidth * 2)/3 ){ style.right = scrollWidth - pageX + 10 + 'px'; }else{ style.left = pageX+10+'px' } if(clientY > (clientHeight * 2) / 3 ){ style.bottom = scrollHeight - pageY + 10 + 'px'; }else{ style.top = pageY + 10 + "px" } this.style = style; this.showModel = true; document.addEventListener('mouseup',this.closeModel) } // 隱藏關(guān)閉此組件 closeModel(){ this.showModel = false; document.removeEventListener('mouseup', this.closeModel); } // 組件銷毀生命周期 destroyed(){ document.removeEventListener('mouseup', this.closeModel); } } </script>
接著,重點(diǎn)來(lái)了,書(shū)寫(xiě)公用封裝函數(shù)
我們要在demo組件點(diǎn)擊時(shí)觸發(fā)這個(gè)函數(shù),即在demo組件里的showMenu事件觸發(fā)函數(shù),這個(gè)函數(shù)要利用createElement和appendChild方法將彈出框創(chuàng)建并插入到頁(yè)面中。
因?yàn)槭屈c(diǎn)擊時(shí)創(chuàng)建并插入元素,所以為了性能優(yōu)化,避免有惡意瘋狂點(diǎn)擊,不斷創(chuàng)建和插入元素,我們利用throttle-debounce插件做一個(gè)節(jié)流。
先直接看代碼,其他注釋寫(xiě)在了代碼里,函數(shù)名隨意?。篗odelFun
// 文件路徑參考: src > components > PublicModel > index.ts import Vue from 'vue'; import PublicModel from './index.vue'; // 導(dǎo)入上面所寫(xiě)的彈框組件 const throttleDebounce = require('throttle-debounce'); // throttle-debounce插件 const debounce = throttleDebounce.debounce; const PublicModelConstructor = Vue.extend(PublicModel); let instance:any; const initInstance = () => { instance = new PublicModelConstructor({ el: document.createElement('div'), }); document.body.appendChild(instance.$el); } const insertInstanceSlot = (slotVNode:any, $event:any) => { // 這里兩個(gè)參數(shù)一個(gè)是彈框里插槽的組件,還有就是點(diǎn)擊的事件對(duì)象(方便定位彈框位置) if(!instance){ initInstance() } instance.$slots.default = [slotVNode]; // 將傳遞過(guò)來(lái)的插槽組件插入彈框組件中 instance.handleOpen($event) // 觸發(fā)彈框組件(見(jiàn)上一段代碼)的彈框獲取定位信息并顯示的事件 } const ModelFun = debounce(200, false, insertInstanceSlot) // 使用throttle-debounce里的debounce保證在一系列調(diào)用時(shí)間中回調(diào)函數(shù)只執(zhí)行一次,這里是200毫秒 // 第二個(gè)參數(shù)為false時(shí),在點(diǎn)擊時(shí)會(huì)在200毫秒后再執(zhí)行callback(即insertInstanceSlot),但為true時(shí),會(huì)立即先執(zhí)行一次; export default ModelFun
接著,重點(diǎn)來(lái)了,書(shū)寫(xiě)公用封裝函數(shù)
我們要在demo組件點(diǎn)擊時(shí)觸發(fā)這個(gè)函數(shù),即在demo組件里的showMenu事件觸發(fā)函數(shù),這個(gè)函數(shù)要利用createElement和appendChild方法將彈出框創(chuàng)建并插入到頁(yè)面中。
因?yàn)槭屈c(diǎn)擊時(shí)創(chuàng)建并插入元素,所以為了性能優(yōu)化,避免有惡意瘋狂點(diǎn)擊,不斷創(chuàng)建和插入元素,我們利用throttle-debounce插件做一個(gè)節(jié)流。
先直接看代碼,其他注釋寫(xiě)在了代碼里,函數(shù)名隨意取:ModelFun
// 文件路徑參考: src > components > PublicModel > index.tsimport Vue from 'vue'; import PublicModel from './index.vue'; // 導(dǎo)入上面所寫(xiě)的彈框組件 const throttleDebounce = require('throttle-debounce'); // throttle-debounce插件 const debounce = throttleDebounce.debounce; const PublicModelConstructor = Vue.extend(PublicModel); let instance:any; const initInstance = () => { instance = new PublicModelConstructor({ el: document.createElement('div'), }); document.body.appendChild(instance.$el); } const insertInstanceSlot = (slotVNode:any, $event:any) => { // 這里兩個(gè)參數(shù)一個(gè)是彈框里插槽的組件,還有就是點(diǎn)擊的事件對(duì)象(方便定位彈框位置) if(!instance){ initInstance() } instance.$slots.default = [slotVNode]; // 將傳遞過(guò)來(lái)的插槽組件插入彈框組件中 instance.handleOpen($event) // 觸發(fā)彈框組件(見(jiàn)上一段代碼)的彈框獲取定位信息并顯示的事件 } const ModelFun = debounce(200, false, insertInstanceSlot) // 使用throttle-debounce里的debounce保證在一系列調(diào)用時(shí)間中回調(diào)函數(shù)只執(zhí)行一次,這里是200毫秒 // 第二個(gè)參數(shù)為false時(shí),在點(diǎn)擊時(shí)會(huì)在200毫秒后再執(zhí)行callback(即insertInstanceSlot),但為true時(shí),會(huì)立即先執(zhí)行一次;export default ModelFun
最后,我們回過(guò)頭來(lái)完善一下demo組件
利用vue的 $createElement 將ActionList組件插入彈框中,并將數(shù)據(jù)和事件傳遞給ActionList組件,這里我們傳遞的事件是簡(jiǎn)單的彈出我們點(diǎn)擊的數(shù)據(jù)
// 文件路徑參考: src > views > demo> index.vue<template> <div class="demo-wrapper"> <div class="demo-div"> <span>更多功能</span> <i class="xk-icon xk-ellipsis" @click.stop='showMenu($event)'></i> </div> </div> </template> <script lang="ts"> import { Vue, Component, Prop, Watch} from "vue-property-decorator"; import ActionList from "@/components/ActionList/index.vue"; import modelFun from "@/components/PublicModel/index"; @Component({ }) export default class articleView extends Vue { menuList: string[] = ['菜單1','菜單2','菜單3']; menuClick(name:string){ // 彈框里插槽的點(diǎn)擊事件 this.$message({message:name,type:'success'}) } showMenu($event:any){ modelFun( this.$createElement( ActionList, { props: { menu:this.menuList }, on:{ click: this.menuClick, } } ), $event ) } }; </script>
至此,效果如下
最后,我們利用element ui 的 tree 組件結(jié)合我們封裝的彈框看一下效果
代碼:
<template> <div class="demo-wrapper"> <el-tree :data="data" node-key="id" :default-expand-all="true" :expand-on-click-node="false" show-checkbox > <div class="custom-tree-node tree-item" iv slot-scope="{ node }"> <span>{{ node.label }}</span> <span class="action" @click.stop="showMenu($event)" > <i class="el-icon-more"></i> </span> </div> </el-tree> </div> </template> <script lang="ts"> import { Vue, Component, Prop, Watch} from "vue-property-decorator"; import ActionList from "@/components/ActionList/index.vue"; import modelFun from "@/components/PublicModel/index"; @Component({ }) export default class articleView extends Vue { menuList: string[] = ['菜單1','菜單2','菜單3']; data:any[] = [{ id: 1, label: '一級(jí) 1', children: [{ id: 4, label: '二級(jí) 1-1', children: [{ id: 9, label: '三級(jí) 1-1-1' }, { id: 10, label: '三級(jí) 1-1-2' }] }] }, { id: 2, label: '一級(jí) 2', children: [{ id: 5, label: '二級(jí) 2-1' }, { id: 6, label: '二級(jí) 2-2' }] }, { id: 3, label: '一級(jí) 3', children: [{ id: 7, label: '二級(jí) 3-1' }, { id: 8, label: '二級(jí) 3-2' }] }]; menuClick(name:string){ console.log(name) this.$message({message:name,type:'success'}) } showMenu($event:any){ modelFun( this.$createElement( ActionList, { props: { menu:this.menuList }, on:{ click: this.menuClick, } } ), $event ) } }; </script>
效果:
以上就是vue實(shí)現(xiàn)點(diǎn)擊出現(xiàn)操作彈出框的示例的詳細(xì)內(nèi)容,更多關(guān)于vue 彈出框的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue3+elementUI實(shí)現(xiàn)懸浮多行文本輸入框效果
這篇文章主要為大家詳細(xì)介紹了vue3如何引用elementUI實(shí)現(xiàn)懸浮文本輸入框效果,以便實(shí)現(xiàn)多行文本輸入,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-10-10非Vuex實(shí)現(xiàn)的登錄狀態(tài)判斷封裝實(shí)例代碼
這篇文章主要給大家介紹了關(guān)于非Vuex實(shí)現(xiàn)的登錄狀態(tài)判斷封裝的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-02-02Vue3.0路由跳轉(zhuǎn)攜帶參數(shù)的示例詳解
這篇文章主要介紹了Vue3.0路由跳轉(zhuǎn)攜帶參數(shù)的示例代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-04-04vue 全局封裝loading加載教程(全局監(jiān)聽(tīng))
這篇文章主要介紹了vue 全局封裝loading加載教程(全局監(jiān)聽(tīng)),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-11-11vuejs使用遞歸組件實(shí)現(xiàn)樹(shù)形目錄的方法
本篇文章主要介紹了vuejs使用遞歸組件實(shí)現(xiàn)樹(shù)形目錄的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-09-09vite打包優(yōu)化CDN壓縮的分析實(shí)現(xiàn)
我們?cè)谌粘5墓ぷ髦锌隙〞?huì)遇到項(xiàng)目打包優(yōu)化等問(wèn)題,本文主要介紹了vite打包優(yōu)化CDN壓縮的分析實(shí)現(xiàn),具有一定的參加價(jià)值,感興趣的可以了解一下2024-07-07