大家好,我是前端西瓜哥。

本文将讲解 Web 页面如何实现自定义菜单功能。

线上 demo:

https://codepen.io/F-star/pen/WNOvQVQ

思路

核心思路是:注册 contextmenu 事件,取消该事件的默认行为,然后通过 event 对象拿到光标相对视口的坐标位置(event.clientXevent.clientY),通过绝对定位的方式,将自己自定义的初始化时不可见的 div 块显示出来。

实现

DOM 结构

首先是 DOM 结构。结构依次为:

  • div.page-view 为注册 contextmenu 事件的元素;

  • div.contextmenu-mask 是遮罩层,遮住整个窗口。它随右键菜单出现而出现,作用是防止用户调出右键菜单后,还可以点击菜单外的按钮。此外还可以添加有透明度的背景色,但这样效果就类似弹窗了。一般来说,都是不设置底色的。

  • div.contextmenu-content 右键菜单的内容。

<div class="page-view">点击区域
</div><div class="contextmenu-mask" style="display: none;"></div>
<div class="contextmenu-content"><div class="list"><div class="item">复制</div><div class="item">剪切</div><div class="item">粘贴粘贴粘贴粘贴粘贴粘贴粘贴粘贴</div><div class="item">全选</div></div>
</div>

CSS 样式

.page-view {margin: 0 auto;width: 90%;height: calc(100vh - 30px);background-color: azure;
}
/* 遮罩层 */
.contextmenu-mask {position: fixed;left: 0;right: 0;top: 0;bottom: 0;/* background-color: #000; *//* opacity: .2; */z-index: 45;
}
/* 菜单内容的容器 */
.contextmenu-content {position: fixed;left: 999999px;top: 999999px;z-index: 50;user-select: none;
}
/* 例子使用内容 */
.list {border: 1px solid #555;border-radius: 4px;min-width: 180px;overflow: hidden; /* 处理圆角 */
}
.item {box-sizing: border-box;padding: 0 5px;height: 30px;line-height: 30px;word-break: keep-all; /* 很重要,否则会换行 */background-color: #fff;cursor: default;
}
.item:hover {background-color: dodgerblue;color: #fff;
}

这里有几个注意点:

  • .contextmenu-content 并没有使用 display: none 的方式进行隐藏,而是通过设置非常大的 left 和 top 的方式跑到窗口外的远方。这是有原因的,我们将会在后面的脚本逻辑中进行详细讲解。

  • .item 需要设置 word-break: keep-all; 。因为当菜单跑到窗口外时,宽度会变成最小宽度,在这里是 180px。只有设置了该属性和值,才能让文字不换行,得到我们想要的宽度。

  • .contextmenu-content 需要使用固定定位,不能使用绝对定位。因为设置了大值的 left 和 top 的元素,对不是 overflow: hidden 的容器元素,会产生一个非常长的滚动条。固定定位则不会。

脚本逻辑

右键显示菜单

首先取消掉点击区域的菜单事件的默认行为。

拿到光标的坐标,为防止菜单部分跑到窗口外,导致被切割,需要对坐标进行调整。对此我们需要再拿到 菜单的宽高窗口可视区域宽高

此外为了防止菜单边缘紧贴窗口边缘,效果不美观,需要设置一个 最小 padding 值 参与计算。

被截断的菜单:

紧贴窗口边缘的菜单:

以设置横坐标为例,有:

if (e.clientX + contextmenuWidth > document.documentElement.clientWidth - PADDING_RIGHT) {finalX = e.clientX - contextmenuWidth
}

这里代码的意思是:当预测发现当前光标作为菜单的左侧时,会导致菜单右侧一部分被切割,就以当前坐标作为菜单的右侧,此时的左上角的坐标为光标减去菜单宽度的值。

完整代码为:

const areaEl = document.querySelector('.page-view')
const mask = document.querySelector('.contextmenu-mask')
const contentEl = document.querySelector('.contextmenu-content')/*** * @param {number} x 将要设置的菜单的左上角坐标 x* @param {number} y 左上角 y* @param {number} w 菜单的宽度* @param {number} h 菜单的高度* @returns {x, y} 调整后的坐标*/
const adjustPos = (x, y, w, h) => {const PADDING_RIGHT = 6  // 右边留点空位,防止直接贴边了,不好看const PADDING_BOTTOM = 6  // 底部也留点空位const vw = document.documentElement.clientWidthconst vh = document.documentElement.clientHeightif (x + w > vw - PADDING_RIGHT) x -= wif (y + h > vh - PADDING_BOTTOM) y -= hreturn {x, y}
}const onContextMenu = e => {e.preventDefault()const rect = contentEl.getBoundingClientRect()// console.log(rect)const { x, y } = adjustPos(e.clientX, e.clientY, rect.width, rect.height)showContextMenu(x, y)
}// 阻止指定元素下的菜单事件
areaEl.addEventListener('contextmenu', onContextMenu, false)

隐藏右键菜单没有使用常规的 display: none;,而是改为使用设置了很大值的 left 和 top。这是因为我要实现的是 自适应宽高 的右键菜单。

为此需要动态拿到菜单的宽高,需要用到 Element.getBoundingClientRect() 方法,而这个方法需要元素在 DOM 树中,且为可见元素,才能拿到宽高,否则只能拿到两个 0。

如果你要实现的菜单是手动写死宽度的,高度通过菜单项的数量来计算的,那么隐藏菜单最好的方案是 display: none

隐藏菜单和点击菜单项

然后就是点击遮罩层,隐藏菜单和遮罩。以及点击菜单项,执行对应的命令

const hideContextMenu = () => {mask.style.display = 'none'contentEl.style.top = '99999px'contentEl.style.left = '99999px'
}// 点击蒙版,隐藏
mask.addEventListener('mousedown', () => {hideContextMenu()
}, false)// 点击菜单,隐藏
contentEl.addEventListener('click', (e) => {console.log('点击:', e.target.textContent)// 执行菜单项对应命令hideContextMenu()
}, false)

其他要考虑的地方

  • 窗口缩小的情况:窗口缩小会导致右下方的菜单跑到窗口区域外,是否考虑监听窗口事件。

  • 菜单上再点右键的逻辑:是以这个位置重新定位右键菜单,还是等同于点击了左键的效果,还是不进行处理,弹出浏览器原生右键菜单,需要根据需求进行选择。

结尾

实现自定义菜单的逻辑并不复杂,也就是修改 contextmenu 事件的行为,显示或隐藏自己写的 div。

但里面有些细节需要处理好,才能写出一个没有 bug 的优秀右键菜单。

前端实现右键自定义菜单相关推荐

  1. 右键脚本html,js实现右键自定义菜单

    本文实例为大家分享了右键自定义菜单的具体代码,供大家参考,具体内容如下 #menu { height: 200px; width: 50px; border: 1px solid gray; back ...

  2. JavaScript学习(三十七)—实现右键自定义菜单的功能

    JavaScript学习(三十七)-实现右键自定义菜单的功能 代码如下: <!DOCTYPE html> <html lang="en"><head& ...

  3. vue右键自定义菜单_一款小巧的开源右键菜单管理软件

    要说右键管理软件,果核上面目前收集了几款,例如年久失修的右键管家. 虽然很多年没有更新了,但是软件的功能却正常,日常删除多余的右键菜单没问题. 另外,就是火绒家的右键管家,基本功能也够用 不过嘛,今天 ...

  4. vue鼠标右键自定义菜单_vue-右键菜单功能

    [TOC] >[success] # :-: vue-右键菜单功能 [在线demo点击我](https://xunleif2e.github.io/vue-context-menu/demo/d ...

  5. vue鼠标右键自定义菜单_使用Vue自定义指令实现右键菜单

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

  6. 【jQuery】smartMenu右键自定义上下文菜单插件(似web QQ)

    DEMO: http://sources.ikeepstudying.com/menu-mail-qq/ 一.这是什么样的一个插件 我们都知道,默认状态下,我们右键web页面,会出现一个上下文菜单,例 ...

  7. 使用Bootstrap制作右键单击自定义菜单

    我们可以轻松地通过右键单击创建一个漂亮的自定义菜单. 让我们创建一个div右键单击启用自定义菜单. <div class="col-lg-6 bg-info" id=&quo ...

  8. Win10 自定义右键新建菜单

    Win10 自定义右键新建菜单 regedit打开: 计算机\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\ ...

  9. Element 组件之 右键鼠标 自定义菜单

    参考链接: Element 组件之 右键鼠标 自定义菜单 Vue+ElementUI实现给Tab页添加鼠标右键菜单栏 Element tree组件之 自定义菜单 基于element tree组件.效果 ...

最新文章

  1. 收藏 | 75道常见AI面试题助你清扫知识盲点(附解析)
  2. 东 北 大 学计算机辅助设计,东北大学计算机辅助设计(B)
  3. 如何在Eclipse和Android Studio中导入library project
  4. 001_汽车之家,新浪和360之间的交流
  5. golang 文件(文件打开,文件写入,文件读取,文件删除)的基本操作
  6. java设计模式--单例设计模式
  7. python中运算的英文_[lemon]Python中的运算符,LemonPython
  8. HyperLogLog原理与在Redis中的使用
  9. linux系统ip占用,IP地址被占用的问题,折腾我好几天了 (已解决)
  10. 程序员上班都在做什么?
  11. 【英语学习】【English L06】U02 Food L3 Peking roast duck
  12. WIN7中文专业版安装英文语言包的方法
  13. 全球票房73亿的《海王》,是怎么炼成的!
  14. VMware Workstation 虚拟机下载及安装的详细步骤
  15. 数模混合计算机应用,数模混合集成电路的发展现状和前景
  16. 七个问题透视百度智慧商业平台
  17. 数据分析细数周杰伦不能说的秘密
  18. C++ Primer Plus 第九章答案 内存模型和名称空间
  19. MATLAB三维画图函数使用总结
  20. 微信企业号的消息发送

热门文章

  1. 实用技巧--CAD制作空心文字【中级】
  2. 网络安全基本知识(转)
  3. jdk8安装教程及环境变量配置
  4. win10上ipa包安装到iPhone手机上的方法
  5. 如何提取到网页上播放的视频
  6. 弹塑性力学--应变硬化
  7. 最新版百度网盘,下载提速方法
  8. 一花一世界 一树一菩提
  9. 大学计算机基础教程魏长宝答案,容易并发脓气胸的是A.肺炎球菌肺炎B.支原体肺炎C.肺炎杆菌肺炎D.绿脓杆菌肺炎E.金黄色葡萄球菌肺...
  10. R语言 ggplot2 多图排列 Part(1)