本文已参与「新人创作礼」活动,一起开启掘金创作之路
前言
右键菜单也算是前端页面中比较常见的功能了,根据需求的不同以及其他原因,将右键菜单功能分为两篇完成,本篇为优化版右键菜单。
上一篇简易版的右键菜单,已完成了右键菜单的基本功能,但它存在溢出的问题。
菜单溢出
菜单溢出就是在页面的边缘触发右键菜单时,由于触发点距离页面边缘距离小于右键菜单的宽度,所产生菜单显示不全的问题。
子菜单溢出
子菜单溢出也是一样的,就是菜单距离页面边缘的距离小于子菜单的宽度时,出现的子菜单显示不全问题。
解决溢出
首先,来回顾一下右键菜单的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>
要解决溢出问题,我们就得先计算一下,menu.style.left
最大值;
由图可知:
当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';
解决子菜单溢出
解决子菜单溢出也是同理,在此基础上再减去子菜单的宽度即可。
const submenu=document.getElementById('submenu');
if(x > (winWidth -menuWidth - submenu.offsetWidth)){
submenu.style.left = '-200px';
}else{
submenu.style.left ='';
submenu.style.right ='-200px';
}
最后效果
示例代码
<!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>
到此优化版的右键菜单功能我们就实现了
如果大家还有什么其他想法,欢迎在评论区交流!