不想要网页默认的右键菜单栏,怎么封装一个可以自定义的右键菜单组件?

说在前面

🎈网页的功能和用途可能各不相同,在传统右键菜单栏中无法满足每个用户的个性化需求。通过自定义右键菜单栏,用户可以根据自己的需求添加、调整和删除菜单选项,以实现个性化定制。通过自定义右键菜单栏,可以为用户提供快速访问常用功能和操作的便捷方式,从而提高用户体验。

效果展示

实现原理

1、oncontextmenu事件了解一下

oncontextmenu 事件在元素中用户右击鼠标时触发并打开上下文菜单。

oncontextmenu是一个DOM事件,它在用户右键点击时触发。可以通过在HTML元素上添加oncontextmenu属性来指定右键菜单的处理函数。

例如,在一个按钮元素上添加oncontextmenu属性:

<button oncontextmenu="showContextMenu(event)">右键点击我</button>

在这个示例中,当用户右键点击按钮时,会调用showContextMenu函数,并将事件对象作为参数传递给该函数。

在JavaScript代码中,可以定义showContextMenu函数来处理右键菜单的显示和操作:

function showContextMenu(event) {
  event.preventDefault(); // 阻止默认的右键菜单弹出
  // 显示自定义右键菜单
  // ...
}

在showContextMenu函数中,通过调用event.preventDefault()方法阻止浏览器默认的右键菜单弹出。然后,可以根据需要执行自定义的逻辑,例如显示自定义的右键菜单。

2、在指定容器元素自定义右键菜单

首先,使用getElementById方法获取绑定右键菜单的DOM元素和右键菜单的容器元素。如果获取失败,则直接返回。

const dom = document.getElementById(this.domId);
if (!dom) return;

接着,给绑定右键菜单的DOM元素添加oncontextmenu事件处理函数。当用户触发右键点击事件时,首先调用hideAllMenu方法隐藏所有的右键菜单,然后通过event.preventDefault方法禁止默认行为,防止浏览器弹出默认的右键菜单。接下来,计算出鼠标指针相对于文档顶部和左侧的位置,并设置右键菜单的位置和显示状态。

const that = this;
dom.oncontextmenu = function (e) {
    that.hideAllMenu(that.uid);
    // 自定义body元素的鼠标事件处理函数
    e = e || window.event;
    e.preventDefault();
    let scrollTop =
        document.documentElement.scrollTop ||
        document.body.scrollTop; // 获取垂直滚动条位置
    let scrollLeft =
        document.documentElement.scrollLeft ||
        document.body.scrollLeft; // 获取水平滚动条位置
    menu.style.display = "block";
    menu.style.left = e.clientX + scrollLeft + "px";
    menu.style.top = e.clientY + scrollTop + "px";
};

最后,给document对象添加onclick事件处理函数。当用户在其他位置点击鼠标时,调用hideAllMenu方法隐藏所有的右键菜单。

document.onclick = function () {
    that.hideAllMenu();
};
hideAllMenu(id) {
    const jMenu = document.getElementsByClassName("j-mouse-menu");
    for (let i = 0; i < jMenu.length; i++) {
        if (jMenu[i].id != id) jMenu[i].style.display = "none";
    }
},

3、封装成一个组件

(1)template菜单模板
<div :id="uid" class="j-mouse-menu">
    <slot name="header"></slot>
    <ul>
        <li
            v-for="menuItem in menu"
            :key="menuItem.id"
            @click="menuClick(menuItem)"
        >
            {{ menuItem.label }}
        </li>
    </ul>
    <slot name="body"></slot>
    <slot name="footer"></slot>
</div>

使用:id="uid"绑定了组件的id属性,该属性值由组件实例的uid属性提供。这样可以确保每个组件实例都有唯一的id。

然后,给组件添加了j-mouse-menu类,用于设置组件的样式。

在组件的内容区域中,使用了Vue的插槽机制。

  • <slot name="header"></slot>定义了一个名为"header"的插槽,用于放置菜单栏的头部内容。
  • <ul>标签下使用了v-for指令遍历menu数组,生成菜单项。
  • 菜单项使用<li>标签表示,并通过:key绑定了唯一的menuItem.id作为key值。
  • 通过@click绑定了menuClick方法,该方法会在点击菜单项时被调用。
  • 菜单项的显示文本使用插值语法{{ menuItem.label }}来动态显示。

接下来,又定义了两个插槽:

  • <slot name="body"></slot>用于放置菜单栏的主体内容。
  • <slot name="footer"></slot>用于放置菜单栏的底部内容。
(2)props入参
props: {
    domId: {
        type: String,
        default: "",
    },
    menu: {
        type: Array,
        default: () => {
            return [];
        },
    },
},

domId表示需要绑定右键菜单的DOM元素容器的id,menu表示右键菜单的选项列表,menu数据格式如下:

[
    {
        id: "1",
        label: "菜单1"
    },
    {
        id: "2",
        label: "菜单2",
        click: this.test
    },
    {
        id: "3",
        label: "菜单3"
    },
    {
        id: "4",
        label: "菜单4"
    },
    {
        id: "5",
        label: "菜单5"
    }
]
(3)菜单点击回调
menuClick(item) {
    if (item.click) {
        item.click(item);
        return;
    }
    this.$emit("menuClick", item);
},

首先判断item对象是否存在click属性。如果存在,则执行item.click(item),并将item作为参数传递给click函数。然后,返回结束方法的执行。

如果item对象不存在click属性,即没有自定义的点击处理函数,那么就通过this.$emit(“menuClick”, item)语法触发一个名为"menuClick"的自定义事件,并将item作为参数传递给父组件。

(4)完整组件代码
<template>
    <div>
        <div :id="uid" class="j-mouse-menu">
            <slot name="header"></slot>
            <ul>
                <li
                    v-for="menuItem in menu"
                    :key="menuItem.id"
                    @click="menuClick(menuItem)"
                >
                    {{ menuItem.label }}
                </li>
            </ul>
            <slot name="body"></slot>
            <slot name="footer"></slot>
        </div>
    </div>
</template>

<script>
import { getUId } from "../../../utils/strTool";
export default {
    name: "JMouseMenu",
    props: {
        domId: {
            type: String,
            default: "",
        },
        menu: {
            type: Array,
            default: () => {
                return [];
            },
        },
    },
    data() {
        return {
            uid: "",
        };
    },
    created() {
        this.setUid();
    },
    mounted() {
        this.init();
    },
    methods: {
        setUid() {
            this.uid = "j-mouse-menu-" + getUId();
        },
        init() {
            // 自定义鼠标右键菜单栏
            const dom = document.getElementById(this.domId);
            if (!dom) return;
            const menu = document.getElementById(this.uid);
            const that = this;
            dom.oncontextmenu = function (e) {
                that.hideAllMenu(that.uid);
                // 自定义body元素的鼠标事件处理函数
                e = e || window.event;
                e.preventDefault();
                let scrollTop =
                    document.documentElement.scrollTop ||
                    document.body.scrollTop; // 获取垂直滚动条位置
                let scrollLeft =
                    document.documentElement.scrollLeft ||
                    document.body.scrollLeft; // 获取水平滚动条位置
                menu.style.display = "block";
                menu.style.left = e.clientX + scrollLeft + "px";
                menu.style.top = e.clientY + scrollTop + "px";
            };
            // 鼠标点击其他位置时隐藏菜单
            document.onclick = function () {
                that.hideAllMenu();
            };
        },
        hideAllMenu(id) {
            const jMenu = document.getElementsByClassName("j-mouse-menu");
            for (let i = 0; i < jMenu.length; i++) {
                if (jMenu[i].id != id) jMenu[i].style.display = "none";
            }
        },
        menuClick(item) {
            if (item.click) {
                item.click(item);
                return;
            }
            this.$emit("menuClick", item);
        },
    },
};
</script>

<style lang="less" scoped>
.j-mouse-menu {
    display: none;
    position: absolute;
    min-width: 8em;
    max-width: 15em;
    border: 1px solid #ccc;
    background: #eee;
    ul {
        margin: 5px 0;
        padding: 0;
    }
    li {
        height: 30px;
        line-height: 30px;
        color: #21232e;
        font-size: 12px;
        text-align: center;
        cursor: default;
        padding: 0;
        margin: 0;
        list-style-type: none;
        border-bottom: 1px dashed #cecece;
        &:hover {
            background-color: #cccccc;
        }
    }
}
</style>
(5)组件使用
<j-mouse-menu
    :domId="'j-mouse-menu-view-content1'"
    :menu="myMenu"
    @menuClick="menuClick"
>
    <template v-slot:header>
        <div class="menu-slot-header">🌝JYeontu</div>
    </template>
    <template v-slot:footer>
        <div class="menu-slot">
            🦁🐼
        </div>
    </template>
</j-mouse-menu>

通过插槽自定义右键菜单的头部和底部内容,菜单列表通过menu参数传入子组件,并绑定菜单点击事件menuClick

data(){
    return {
        myMenu: [
            {
                id: "1",
                label: "菜单1"
            },
            {
                id: "2",
                label: "菜单2",
                click: this.test
            },
            {
                id: "3",
                label: "菜单3"
            },
            {
                id: "4",
                label: "菜单4"
            },
            {
                id: "5",
                label: "菜单5"
            }
        ]
    }
},
methods:{
    menuClick(menuItem) {
        alert("点击了:" + menuItem.label);
    },
    test(menuItem) {
        alert("test-" + menuItem.id);
    },
    alert(label) {
        alert("点击了:" + label);
    }
}

组件库

组件文档

目前该组件也已经收录到我的组件库,组件文档地址如下:
http://jyeontu.xyz/jvuewheel/#/JMouseMenu

组件内容

组件库中还有许多好玩有趣的组件,如:

  • 悬浮按钮
  • 评论组件
  • 词云
  • 瀑布流照片容器
  • 视频动态封面
  • 3D轮播图
  • web桌宠
  • 贡献度面板
  • 拖拽上传
  • 自动补全输入框
  • 图片滑块验证

等等……

组件库源码

组件库已开源到gitee,有兴趣的也可以到这里看看:https://gitee.com/zheng_yongtao/jyeontu-component-warehouse

觉得有帮助的可以点个star~

有什么问题或错误可以指出,欢迎pr~

有什么想要实现的组件或想法可以联系我~

公众号

关注公众号『前端也能这么有趣』,获取更多有趣内容。

发送『组件库』获取源码

说在后面

🎉 这里是 JYeontu,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打羽毛球 🏸 ,平时也喜欢写些东西,既为自己记录 📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解 🙇,写错的地方望指出,定会认真改进 😊,偶尔也会在自己的公众号『前端也能这么有趣』发一些比较有趣的文章,有兴趣的也可以关注下。在此谢谢大家的支持,我们下文再见 🙌。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/341305.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何使用 Helm 在 K8s 上集成 Prometheus 和 Grafana|Part 3

在本教程的前两部分&#xff0c;我们分别了解和学习了Prometheus 和 Grafana 的基本概念和使用的前提条件&#xff0c;以及使用 Helm 在 Kubernetes 上安装 Prometheus。 在今天的教程中&#xff0c;我们将为你介绍以下内容&#xff1a; 安装 Grafana&#xff1b;集成 Promethe…

centos 启动nacos pg版本

背景&#xff1a;支持国产化需求&#xff0c;不再使用mysql 1.修改插件 git clone https://github.com/wuchubuzai2018/nacos-datasource-extend-plugins.git cd nacos-datasource-extend-plugins/nacos-postgresql-datasource-plugin-ext mvn package编译成功后&#xff0c;…

Docker(七)使用网络

作者主页&#xff1a; 正函数的个人主页 文章收录专栏&#xff1a; Docker 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01; Docker 中的网络功能介绍 Docker 允许通过外部访问容器或容器互联的方式来提供网络服务。 一、外部访问容器 容器中可以运行一些网络应用&…

代码随想录算法训练营29期|day27 任务以及具体安排

39. 组合总和// 剪枝优化 class Solution {public List<List<Integer>> combinationSum(int[] candidates, int target) {List<List<Integer>> res new ArrayList<>();Arrays.sort(candidates); // 先进行排序backtracking(res, new ArrayList&…

NetSuite 文心一言(Ernie)的AI应用

有个故事&#xff0c;松下幸之助小时候所处的年代是明治维新之后&#xff0c;大量引用西洋技术的时期。当时大家对“电”能干什么事&#xff0c;充满好奇。“电能干什么&#xff1f;它能帮我们开门么&#xff1f;” 松下幸之助的爷爷对电不屑&#xff0c;于是就问他。松下幸之助…

仓储管理系统——软件工程报告(可行性研究报告及分析)①

可行性研究报告及分析 一、问题定义 1.1项目背景 随着社会的发展以及企业规模的扩大和业务的复杂化&#xff0c;仓库管理变得愈发重要。传统的手工管理方式已经导致了一系列问题&#xff0c;包括库存准确性低、订单处理效率慢等。为了提高仓库运作效率、降低成本并优化库存管…

Qt5.12.0 与 VS2017 在 .pro文件转.vcxproj文件

一、参考资料 stackoverflow qt - How to generate .sln/.vcproj using qmake - Stack Overflowhttps://stackoverflow.com/questions/2339832/how-to-generate-sln-vcproj-using-qmake?answertabtrending#tab-topqt - 如何使用 qmake 生成 .sln/.vcproj - IT工具网 (coder.wo…

搜索与图论第六期 最短路问题

前言 最短路问题真的很重要很重要希望大家都能够完全掌握所有最短路算法&#xff01;&#xff01; 一、最短路问题的分类 Dijkstra&#xff1a; Dijkstra算法是一种著名的图算法&#xff0c;主要用于求解有权图中的单源最短路径问题。它由荷兰计算机科学家艾兹赫尔戴克斯特…

(十)Head first design patterns组合模式(c++)

组合模式 组合模式在参考链接中已经讲得很好了&#xff0c;这里只简单讲讲就好。 组合模式的意图是表达部分-整体层次结构。 当你需要管理一个组合对象&#xff0c;又要管理这个组合对象的单个对象。这个时候就可以让这个组合对象和单个对象继承同一个基类&#xff0c;以便用…

pytorch学习笔记(十一)

优化器学习 把搭建好的模型拿来训练&#xff0c;得到最优的参数。 import torch.optim import torchvision from torch import nn from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear from torch.utils.data import DataLoaderdataset torchvision.datas…

E. Increasing Subsequences

Part1 寒假思维训练之每日一道构造题&#xff08;思维 构造 数学&#xff09;题目链接&#xff1a; Problem - E - Codeforces 题意&#xff1a; 给定一个整数&#xff0c;数字n的范围是&#xff0c;闭区间&#xff0c;要求构造一个递增子序列&#xff08;可以不连续&…

在Python环境中运行R语言的配环境实用教程

前情提要 在做一些生物信息与医学统计的工作&#xff0c;本来偷懒希望只靠python完成的&#xff0c;结果还是需要用R语言&#xff0c;倒腾了一会儿&#xff0c;调成功了&#xff0c;就记录一下这个过程。 我的环境&#xff1a; win10, pycharm, R-4.3.2 首先&#xff0c;我们…

proxy 代理的接口报错301问题

项目系统里仅仅这个接口报错&#xff0c;反向代理错误导致。 默认情况下&#xff0c;不接受运行在HTTPS上&#xff0c;且使用了无效证书的后端服务器。如果你想要接受&#xff0c;修改配置&#xff1a;secure: false&#xff08;简单意思&#xff1a;如果本地没有进行过https相…

Armv8-M的TrustZone技术之内存属性单元

如果处理器包含Armv8-M安全扩展&#xff0c;则内存区域的安全状态由内部安全属性单元&#xff08;SAU&#xff0c;Secure Attribution Unit&#xff09;或外部实现定义的属性单元&#xff08;IDAU&#xff0c;Implementation Defined Attribution Unit&#xff09;的组合控制。…

如何在 Ubuntu 22.04 上安装 Apache Web 服务器

前些天发现了一个人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;最重要的屌图甚多&#xff0c;忍不住分享一下给大家。点击跳转到网站。 如何在 Ubuntu 22.04 上安装 Apache Web 服务器 介绍 Apache HTTP 服务器是世界上使用最广泛的 Web 服务器。它…

苹果眼镜(Vision Pro)的开发者指南(3)-【3D UI SwiftUI和RealityKit】介绍

为了更深入地理解SwiftUI和RealityKit,建议你参加专注于SwiftUI场景类型的系列会议。这些会议将帮助你掌握如何在窗口、卷和空间中构建出色的用户界面。同时,了解Model 3D API将为你提供更多关于如何为应用添加深度和维度的知识。此外,通过学习RealityView渲染3D内容,你将能…

8.前端--CSS-显示模式

元素的显示模式 元素显示模式就是元素&#xff08;标签&#xff09;以什么方式进行显示&#xff0c;比如<div>自己占一行&#xff0c;比如一行可以放多个<span>。 1.块元素 常见的块元素 常见的块元素&#xff1a;<h1>~<h6>、<p>、<div>、…

01 Redis的特性

1.1 NoSQL NoSQL&#xff08;“non-relational”&#xff0c; “Not Only SQL”&#xff09;&#xff0c;泛指非关系型的数据库。 键值存储数据库 &#xff1a; 就像 Map 一样的 key-value 对。如Redis文档数据库 &#xff1a; NoSQL 与关系型数据的结合&#xff0c;最像关系…

大模型的学习路线图推荐—多维度深度分析【云驻共创】

&#x1f432;本文背景 近年来&#xff0c;随着深度学习技术的迅猛发展&#xff0c;大模型已经成为学术界和工业界的热门话题。大模型具有数亿到数十亿的参数&#xff0c;这使得它们在处理复杂任务时表现得更为出色&#xff0c;但同时也对计算资源和数据量提出了更高的要求。 …

二、arcgis 点shp数据处理

在工作中&#xff0c;很多时候客户会提供点坐标&#xff0c;那么要想把点坐标生成shp文件&#xff0c;有两种方法&#xff08;坐标系CGCS2000&#xff09;&#xff1a; 1.当只有个位数的点坐标时&#xff0c;可以直接在arcgisMap中添加&#xff0c;具体步骤如下&#xff1a; …
最新文章