快捷搜索:  汽车  科技

前端开发自定义标签(前端实现右键自定义菜单)

前端开发自定义标签(前端实现右键自定义菜单)拿到光标的坐标,为防止菜单部分跑到窗口外,导致被切割,需要对坐标进行调整。对此我们需要再拿到 菜单的宽高、窗口可视区域宽高。首先取消掉点击区域的菜单事件的默认行为。核心思路是:注册 contextmenu 事件,取消该事件的默认行为,然后通过 event 对象拿到光标相对视口的坐标位置(event.clientX 和 event.clientY),通过绝对定位的方式,将自己自定义的初始化时不可见的 div 块显示出来。首先是 DOM 结构。结构依次为:<divclass="page-view"> 点击区域 </div> <divclass="contextmenu-mask"style="display:none;"></div> <divclass="contextmenu-co

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

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

线上 demo:

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

前端开发自定义标签(前端实现右键自定义菜单)(1)

思路

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

实现DOM 结构

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

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

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

.page-view{ margin:0auto; 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:1pxsolid#555; border-radius:4px; min-width:180px; overflow:hidden;/*处理圆角*/ } .item{ box-sizing:border-box; padding:05px; 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 值 参与计算。

被截断的菜单:

前端开发自定义标签(前端实现右键自定义菜单)(2)

紧贴窗口边缘的菜单:

前端开发自定义标签(前端实现右键自定义菜单)(3)

以设置横坐标为例,有:

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

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

前端开发自定义标签(前端实现右键自定义菜单)(4)

完整代码为:

constareaEl=document.querySelector('.page-view') constmask=document.querySelector('.contextmenu-mask') constcontentEl=document.querySelector('.contextmenu-content') /** * *@param{number}x将要设置的菜单的左上角坐标x *@param{number}y左上角y *@param{number}w菜单的宽度 *@param{number}h菜单的高度 *@returns{x y}调整后的坐标 */ constadjustPos=(x y w h)=>{ constPADDING_RIGHT=6//右边留点空位,防止直接贴边了,不好看 constPADDING_BOTTOM=6//底部也留点空位 constvw=document.documentElement.clientWidth constvh=document.documentElement.clientHeight if(x w>vw-PADDING_RIGHT)x-=w if(y h>vh-PADDING_BOTTOM)y-=h return{x y} } constonContextMenu=e=>{ e.preventDefault() constrect=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。

隐藏菜单和点击菜单项

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

consthideContextMenu=()=>{ 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 的优秀右键菜单。

猜您喜欢: