使用Vue自定义指令实现右键菜单

简介: 使用Vue自定义指令实现右键菜单

前言


浏览器里右键时会有一个默认的菜单,在我的开源项目中正好有自定义右键菜单的需求,在npm库找了下与之相关的包,发现都是以组件形式实现的,感觉那种做法太过繁琐。


于是,我就想着能不能像vue的内置指令那样,绑定到元素上,在这个元素上右键就能出现右键菜单,这样做就方便很多了。


看了下vue的自定义指令文档后,经过一番折腾,终于实现我的这个想法,本文就跟大家分享下我的实现思路以及过程,欢迎各位感兴趣的开发者阅读本文。


实现思路


Vue中有很多内置指令,例如:v-ifv-forv-model,它除了这些内置指令外,还允许我们开发者自己注册指令,来实现我们想实现的效果,对Vue自定义指令不熟悉的开发者可以先看一下文档:自定义指令。


接下来,就跟大家讲一下我的实现思路:


  • 布局右键菜单,编写样式
  • 将右键菜单需要的用到的数据在vuex中进行定义
  • 全局注册一个指令,命名为rightClick
  • 拦截被绑定元素的oncontextmenu事件,对组件传过来的值进行处理
  • 更新vuex里的右键菜单数据,触发右键菜单显示


实现过程


接下来,就跟大家分享下我的实现过程。


布局右键样式


我们先来看看这个组件需要哪些数据才能让其显示在鼠标所点的位置。


  • 它的显隐状态,即:元素css的display属性
  • 它的位置,即:元素css的lefttop属性
  • 它的文本数据,即:右键菜单要展示的内容,通过v-for来渲染
  • 它的事件处理函数,即:右键菜单中选项点击时,要进行的事件处理


我们在项目中找一个公用组件,确保这个组件会被渲染,在组件的template中加入下述代码。


<!--右键菜单-->
    <div
      id="rightMenuDom"
      class="right-menu"
      :style="{
        display: rightMenuStatus,
        top: rightMenuTop,
        left: rightMenuLeft
      }"
    >
      <ul>
        <!--分为2组渲染-->
        <li>
          <span
            v-for="item in rightMenuList"
            :key="item.id"
            v-show="item.id <= 3"
            @click="item.handler"
            >{{ item.text }}
          </span>
        </li>
        <li>
          <span
            v-for="item in rightMenuList"
            :key="item.id"
            v-show="item.id > 3"
            @click="item.handler"
            >{{ item.text }}
          </span>
        </li>
      </ul>
    </div>
  </div>


随后,在组件的mounted生命周期中添加全局点击事件的监听,目的是在点击任意位置后隐藏右键菜单。


mounted() {
    // 监听全局点击事件
    document.addEventListener("click", () => {
      // 隐藏右键菜单
      this.$store.commit("updateRightMenuStatus", {
        status: "none",
        left: "0px",
        top: "0px"
      });
    });
  }


紧接着,在组件的computed中获取Vuex中定义的数据,用于渲染右键菜单。


computed: {
    // 右键菜单显隐状态
    rightMenuStatus(): string {
      return this.$store.state.rightMenu.status;
    },
    // 右键菜单距离浏览器顶部高度
    rightMenuTop(): string {
      return this.$store.state.rightMenu.top;
    },
    // 右键菜单距离浏览器左边长度
    rightMenuLeft(): string {
      return this.$store.state.rightMenu.left;
    },
    // 右键菜单列表内容
    rightMenuList(): [] {
      return this.$store.state.rightMenu.list;
    }
  }


最后,给它编写css样式。


// 右键菜单样式
  .right-menu {
    position: fixed;
    left: 0;
    top: 0;
    width: 166px;
    height: auto;
    background-color: rgb(242, 242, 242);
    border: solid 1px #C2C1C2;
    box-shadow: 0 10px 10px #C2C1C2;
    display: none;
    border-radius: 5px;
    ul {
      padding: 0;
      margin: 0;
      font-size: 15px;
      li {
        list-style: none;
        box-sizing: border-box;
        padding: 6px 0;
        border-bottom: 1px solid rgb(216, 216, 217);
        &:nth-child(1) {
          padding-top: 2px;
        }
        &:nth-last-child(1) {
          border-bottom: none;
        }
        span {
          display: block;
          height: 20px;
          line-height: 20px;
          padding-left: 16px;
          &:hover {
            background-color: #0070F5;
            cursor: pointer;
            color: #FFFFFF;
          }
        }
      }
    }
  }


在Vuex中定义数据


在vuex的配置文件中,找到state属性,添加下述代码。


  • status组件的显隐状态
  • top组件距离浏览器可视区域顶部的距离
  • left距离浏览器可视区域左边的距离
  • list组件需要的文本数据和与之对应的事件处理函数


rightMenu: {
  status: "none",
  top: "0px",
  left: "0px",
  list: []
}


随后在mutations中添加更新数据的方法。


// 更新右键菜单数据
updateRightMenuStatus(state, menuObj: rightMenuAttribute) {
  state.rightMenu.status = menuObj.status;
  state.rightMenu.top = menuObj.top;
  state.rightMenu.left = menuObj.left;
  state.rightMenu.list = menuObj.list;
}


注册全局指令


我们在vue的入口文件:main.ts中,注册一个全局指令rightClick


  • el为我们绑定指令的元素
  • binding里包含了指令传过来的参数


app.directive("rightClick", (el, binding) => {
});


拦截右键事件处理指令参数


上面我们注册了一个全局指令,我们需要在它的函数内部为指令所绑定的元素重写其点击事件,处理指令传过来的参数。


  • 将事件对象放进一个数组中
  • 将每一个右键菜单的文本数据和与之对应的时间处理函数放进json数组中
  • 获取鼠标点击的位置,使用commit更新Vuex中的相关数据,渲染页面


el.oncontextmenu = function(e: MouseEvent) {
    const textArray = binding.value.text;
    const handlerObj = binding.value.handler;
    // 事件处理数组
    const handlerArray = [];
    // 处理好的右键菜单
    const menuList = [];
    // 将事件处理函数放入数组中
    for (const key in handlerObj) {
      handlerArray.push(handlerObj[key]);
    }
    // 追加右键菜单数据
    for (let i = 0; i < textArray.length; i++) {
      // 右键菜单对象, 添加名称
      const menuObj = {
        text: textArray[i],
        handler: handlerArray[i],
        id: i + 1
      };
      menuList.push(menuObj);
    }
    // 鼠标点的坐标
    const oX = e.clientX;
    const oY = e.clientY;
    // 右键菜单出现后的位置
    store.commit("updateRightMenuStatus", {
      status: "block",
      left: oX + "px",
      top: oY + "px",
      list: menuList
    });
    return false;
  };


在组件中使用指令


完成上述操作后,我们就已经实现了右键自定义菜单的指令,接下来,我们来看看如何在组件中使用我们注册的指令。


在你想要绑定右键菜单的html元素上添加v-right-click,如下所示:


<li
  class="row-panel"
  v-right-click="rightMenuObj"
>
</li>


在组件的data中定义右键菜单需要的数据,即上面代码中声名的rightMenuObj


// 右键菜单对象,菜单内容和处理事件
rightMenuObj: {
  text: [
    "查看资料",
    "复制用户id",
    "移除该会话",
    "在联系人中查看",
    "在单聊窗口中打开",
    "会话置顶"
  ],
  handler: {
    checkingData() {
      console.log("查看资料点击事件");
    },
    copyId() {
      console.log("复制用户id点击事件");
    },
    removeItem() {
      console.log("移除会话点击事件");
    },
    showContact() {
      console.log("在联系人中查看");
    },
    showSingleChat() {
      console.log("在单聊窗口中打开");
    },
    topConversation() {
      console.log("会话置顶");
    }
  }
}


随后,我们就可以运行看效果了,如下所示,已经成功实现了我们想要的效果。


图片过大,微信无法加载,可点击下方阅读原文进行查看。


代码地址


本文中演示所用的组件GitHub地址如下:

msg-list.vue

main.ts

main-content.vue

main-content.scss

index.ts


写在最后


  • 公众号无法外链,如果文中有链接,可点击下方阅读原文查看😊
相关文章
|
9天前
|
缓存 JavaScript 前端开发
vue学习第四章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中计算属性的基本与复杂使用、setter/getter、与methods的对比及与侦听器的总结。如果你觉得有用,请关注我,将持续更新更多优质内容!🎉🎉🎉
vue学习第四章
|
9天前
|
JavaScript 前端开发
vue学习第九章(v-model)
欢迎来到我的博客,我是瑞雨溪,一名热爱JavaScript与Vue的大一学生,自学前端2年半,正向全栈进发。此篇介绍v-model在不同表单元素中的应用及修饰符的使用,希望能对你有所帮助。关注我,持续更新中!🎉🎉🎉
vue学习第九章(v-model)
|
9天前
|
JavaScript 前端开发 开发者
vue学习第十章(组件开发)
欢迎来到瑞雨溪的博客,一名热爱JavaScript与Vue的大一学生。本文深入讲解Vue组件的基本使用、全局与局部组件、父子组件通信及数据传递等内容,适合前端开发者学习参考。持续更新中,期待您的关注!🎉🎉🎉
vue学习第十章(组件开发)
|
14天前
|
JavaScript 前端开发
如何在 Vue 项目中配置 Tree Shaking?
通过以上针对 Webpack 或 Rollup 的配置方法,就可以在 Vue 项目中有效地启用 Tree Shaking,从而优化项目的打包体积,提高项目的性能和加载速度。在实际配置过程中,需要根据项目的具体情况和需求,对配置进行适当的调整和优化。
|
15天前
|
存储 缓存 JavaScript
如何在大型 Vue 应用中有效地管理计算属性和侦听器
在大型 Vue 应用中,合理管理计算属性和侦听器是优化性能和维护性的关键。本文介绍了如何通过模块化、状态管理和避免冗余计算等方法,有效提升应用的响应性和可维护性。
|
14天前
|
JavaScript 前端开发 UED
vue学习第二章
欢迎来到我的博客!我是一名自学了2年半前端的大一学生,熟悉JavaScript与Vue,目前正在向全栈方向发展。如果你从我的博客中有所收获,欢迎关注我,我将持续更新更多优质文章。你的支持是我最大的动力!🎉🎉🎉
|
14天前
|
JavaScript 前端开发 开发者
vue学习第一章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript和Vue的大一学生。自学前端2年半,熟悉JavaScript与Vue,正向全栈方向发展。博客内容涵盖Vue基础、列表展示及计数器案例等,希望能对你有所帮助。关注我,持续更新中!🎉🎉🎉
|
JavaScript 数据安全/隐私保护
Vue:几个实用的 Vue 自定义指令
Vue:几个实用的 Vue 自定义指令
111 0
Vue:几个实用的 Vue 自定义指令
|
15天前
|
存储 缓存 JavaScript
在 Vue 中使用 computed 和 watch 时,性能问题探讨
本文探讨了在 Vue.js 中使用 computed 计算属性和 watch 监听器时可能遇到的性能问题,并提供了优化建议,帮助开发者提高应用性能。
|
15天前
|
存储 缓存 JavaScript
Vue 中 computed 和 watch 的差异
Vue 中的 `computed` 和 `watch` 都用于处理数据变化,但使用场景不同。`computed` 用于计算属性,依赖于其他数据自动更新;`watch` 用于监听数据变化,执行异步或复杂操作。
下一篇
无影云桌面