IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> JS实现页面右键菜单 -> 正文阅读

[JavaScript知识库]JS实现页面右键菜单

前言

右键菜单也算是前端页面中比较常见的功能了,今天我就带大家完整的实现右键菜单功能。

contextmenu事件

实现右键菜单我们需要用到 oncontextmenu事件,oncontextmenu事件会在用户右击鼠标时触发。

阻止原有菜单

首先,我们将其绑定到window上:window.oncontextmenu

然后,通过event.preventDefault() 方法阻止掉原有的右键菜单,为我们绑定上新的功能做好准备工作。

         window.oncontextmenu = function(e){
            //取消默认的浏览器自带右键
            e.preventDefault();
        }

创建菜单

创建对应菜单的dom结构,并绑定上对应的功能函数:

    <div id="menu" class="menu">
        <div class="menu__item" onclick="log('1')">功能1</div>
        <div class="menu__item" onclick="log('2')">功能2</div>
        <div class="menu__item" onclick="log('3')">功能3</div>
        <div class="menu__item">功能4 <span class="icon"> > </span>
            <div id="submenu" class="submenu">
                <div class="submenu__item" onclick="log('4-1')">功能4-1</div>
                <div class="submenu__item" onclick="log('4-2')">功能4-2</div>
                <div class="submenu__item" onclick="log('4-3')">功能4-3</div>
                <div class="submenu__item" onclick="log('4-4')">功能4-4</div>
            </div>
        </div>
        <div class="menu__item" onclick="log(5)">功能5</div>
    </div>

当然,现在它还是固定的,不能移动。

菜单随鼠标联动

我们将菜单绝对定位,然后利用event的clientX/clientY 属性,将获取的触发点坐标动态赋给菜单的top/left属性,这样菜单就可以跟鼠标联动了。

        const menu=document.getElementById('menu');
        //根据事件对象中鼠标点击的位置,进行定位
        menu.style.left=e.clientX+'px';
        menu.style.top=e.clientY+'px';

现在菜单虽然能跟鼠标联动了,但它现在还不能在默认情况下隐藏。

怎么办?

菜单显隐

很简单,我们先将菜单通过visibility: hidden;隐藏;

        .menu{
            position: absolute;
            top:0px;
            left: 0px;
            background: #fff;
            border: 1px solid #dadce0;
            visibility: hidden;
        }
        .active{
            visibility: visible;
        }

然后再触发 window.oncontextmenu的时候,添加active显示菜单;

     menu.classList.add('active');

现在,菜单功能基本完成了,只需要在 window 上绑定点击事件,然后将active移出,即可隐藏掉菜单;

        //关闭右键菜单
        window.addEventListener('click', function() {
            menu.classList.remove('active');
        })

右键菜单简易版.gif

到此右键菜单功能的基本功能我们就实现了,但它存在溢出的问题。

菜单溢出

菜单溢出就是在页面的边缘触发右键菜单时,由于触发点距离页面边缘距离小于右键菜单的宽度,所产生菜单显示不全的问题。

右键菜单溢出.gif

子菜单溢出

子菜单溢出也是一样的,就是菜单距离页面边缘的距离小于子菜单的宽度时,出现的子菜单显示不全问题。

右键子菜单菜单溢出.gif

解决溢出

要解决溢出问题,我们就得先计算一下,menu.style.left最大值;

image.png

由图可知:

e.offsetX == window.innerWidth - menu.offsetWidth时,菜单刚好不会溢出,

e.offsetX > window.innerWidth - menu.offsetWidth时,菜单就会溢出;

所以,menu.style.left最大值是window.innerWidth - menu.offsetWidth

即,当e.offsetX >= window.innerWidth - menu.offsetWidth时,menu.style.left = window.innerWidth - menu.offsetWidth ;

e.offsetX < window.innerWidth - menu.offsetWidth时,menu.style.left = e.offsetX

    const menu=document.getElementById('menu');
    let x = e.offsetX;                //触发点到页面窗口左边的距离
    let winWidth = window.innerWidth; //窗口的内部宽度(包括滚动条)
    let menuWidth = menu.offsetWidth; //菜单宽度,高度包含内边距(padding)和边框(border),不包含外边距(margin)
    x = winWidth - menuWidth >= x ? x : winWidth -menuWidth;
    menu.style.left = x +'px';

解决子菜单溢出

解决子菜单溢出也是同理,在此基础上再减去子菜单的宽度即可。

image.png

    const submenu=document.getElementById('submenu');
    if(x > (winWidth -menuWidth - submenu.offsetWidth)){
        submenu.style.left = '-200px';
    }else{
        submenu.style.left ='';
        submenu.style.right ='-200px';
    }

最后效果

最后效果.gif

示例代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .menu{
            position: absolute;
            top:0px;
            left: 0px;
            background: #fff;
            border: 1px solid #dadce0;
            visibility: hidden;
        }
        .active{
            visibility: visible;
        }
        .menu__item, .submenu__item{
            width: 200px;
            height: 20px;
        }
        .menu__item:hover, .submenu__item:hover{
            background-color: #dadce0;
        }
        .menu__item:hover .submenu{
            opacity: 1;
        }
        .submenu{
            position: relative;
            top:-20px;
            left: 200px;
            opacity: 0;
            background: #fff;
            border: 1px solid #dadce0;
        }
        .icon{
            position: absolute;
            right: 3px;
        }
    </style>
</head>
<body>
    <div id="menu" class="menu">
        <div class="menu__item" onclick="log('1')">功能1</div>
        <div class="menu__item" onclick="log('2')">功能2</div>
        <div class="menu__item" onclick="log('3')">功能3</div>
        <div class="menu__item">功能4 <span class="icon"> > </span>
            <div id="submenu" class="submenu">
                <div class="submenu__item" onclick="log('4-1')">功能4-1</div>
                <div class="submenu__item" onclick="log('4-2')">功能4-2</div>
                <div class="submenu__item" onclick="log('4-3')">功能4-3</div>
                <div class="submenu__item" onclick="log('4-4')">功能4-4</div>
            </div>
        </div>
        <div class="menu__item" onclick="log(5)">功能5</div>
    </div>
    <script>
        window.onload = function() {
            const menu=document.getElementById('menu');
            const submenu=document.getElementById('submenu');
            window.oncontextmenu=function(e){
                //取消默认的浏览器自带右键
                e.preventDefault();
                let x = e.offsetX;                //触发点到页面窗口左边的距离
                let y = e.offsetY;
                let winWidth = window.innerWidth; //窗口的内部宽度(包括滚动条)
                let winHeight = window.innerHeight;
                let menuWidth = menu.offsetWidth; //菜单宽度
                let menuHeight = menu.offsetHeight;
                x = winWidth - menuWidth >= x ? x : winWidth -menuWidth;
                y = winHeight - menuHeight >= y ? y : winHeight - menuHeight;
                menu.style.top = y+'px';
                menu.style.left = x +'px';
                if(x > (winWidth -menuWidth - submenu.offsetWidth)){
                    submenu.style.left = '-200px';
                }else{
                    submenu.style.left ='';
                    submenu.style.right ='-200px';
                }
                menu.classList.add('active');
            }
            // 关闭右键菜单
            window.addEventListener('click', function() {
                menu.classList.remove('active');
            })
        }
        // 菜单功能测试
        function log(i){
            alert(i);
        }
    </script>
</body>
</html>

到此完整的右键菜单功能我们就实现了

如果大家还有什么其他想法,欢迎在评论区交流!

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-05-21 18:52:15  更:2022-05-21 18:54:49 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 20:39:49-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码