前端实现右键自定义菜单
大家好,我是前端西瓜哥。
本文将讲解 Web 页面如何实现自定义菜单功能。
线上 demo:
https://codepen.io/F-star/pen/WNOvQVQ
思路
核心思路是:注册 contextmenu 事件,取消该事件的默认行为,然后通过 event 对象拿到光标相对视口的坐标位置(event.clientX
和 event.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 的优秀右键菜单。
前端实现右键自定义菜单相关推荐
- 右键脚本html,js实现右键自定义菜单
本文实例为大家分享了右键自定义菜单的具体代码,供大家参考,具体内容如下 #menu { height: 200px; width: 50px; border: 1px solid gray; back ...
- JavaScript学习(三十七)—实现右键自定义菜单的功能
JavaScript学习(三十七)-实现右键自定义菜单的功能 代码如下: <!DOCTYPE html> <html lang="en"><head& ...
- vue右键自定义菜单_一款小巧的开源右键菜单管理软件
要说右键管理软件,果核上面目前收集了几款,例如年久失修的右键管家. 虽然很多年没有更新了,但是软件的功能却正常,日常删除多余的右键菜单没问题. 另外,就是火绒家的右键管家,基本功能也够用 不过嘛,今天 ...
- vue鼠标右键自定义菜单_vue-右键菜单功能
[TOC] >[success] # :-: vue-右键菜单功能 [在线demo点击我](https://xunleif2e.github.io/vue-context-menu/demo/d ...
- vue鼠标右键自定义菜单_使用Vue自定义指令实现右键菜单
前言 浏览器里右键时会有一个默认的菜单,在我的开源项目中正好有自定义右键菜单的需求,在npm库找了下与之相关的包,发现都是以组件形式实现的,感觉那种做法太过繁琐. 于是,我就想着能不能像vue的内置指 ...
- 【jQuery】smartMenu右键自定义上下文菜单插件(似web QQ)
DEMO: http://sources.ikeepstudying.com/menu-mail-qq/ 一.这是什么样的一个插件 我们都知道,默认状态下,我们右键web页面,会出现一个上下文菜单,例 ...
- 使用Bootstrap制作右键单击自定义菜单
我们可以轻松地通过右键单击创建一个漂亮的自定义菜单. 让我们创建一个div右键单击启用自定义菜单. <div class="col-lg-6 bg-info" id=&quo ...
- Win10 自定义右键新建菜单
Win10 自定义右键新建菜单 regedit打开: 计算机\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\ ...
- Element 组件之 右键鼠标 自定义菜单
参考链接: Element 组件之 右键鼠标 自定义菜单 Vue+ElementUI实现给Tab页添加鼠标右键菜单栏 Element tree组件之 自定义菜单 基于element tree组件.效果 ...
最新文章
- 收藏 | 75道常见AI面试题助你清扫知识盲点(附解析)
- 东 北 大 学计算机辅助设计,东北大学计算机辅助设计(B)
- 如何在Eclipse和Android Studio中导入library project
- 001_汽车之家,新浪和360之间的交流
- golang 文件(文件打开,文件写入,文件读取,文件删除)的基本操作
- java设计模式--单例设计模式
- python中运算的英文_[lemon]Python中的运算符,LemonPython
- HyperLogLog原理与在Redis中的使用
- linux系统ip占用,IP地址被占用的问题,折腾我好几天了 (已解决)
- 程序员上班都在做什么?
- 【英语学习】【English L06】U02 Food L3 Peking roast duck
- WIN7中文专业版安装英文语言包的方法
- 全球票房73亿的《海王》,是怎么炼成的!
- VMware Workstation 虚拟机下载及安装的详细步骤
- 数模混合计算机应用,数模混合集成电路的发展现状和前景
- 七个问题透视百度智慧商业平台
- 数据分析细数周杰伦不能说的秘密
- C++ Primer Plus 第九章答案 内存模型和名称空间
- MATLAB三维画图函数使用总结
- 微信企业号的消息发送