自定义右键菜单学习

作者 Simmin 日期 2016-07-22
自定义右键菜单学习

一、一般思路

严格来讲,右键菜单应该叫上下文菜单,在html5中也新定义了这个contextmenu全局属性。

它的语法是这样的:

<p contextmenu="supermenu">这个段落附加了一个名为 "supermenu" 的上下文菜单。</p>
<menu id="supermenu">
<command label="Step 1: Write Tutorial" onclick="doSomething()">
<command label="Step 2: Edit Tutorial" onclick="doSomethingElse()">
</menu>

contextmenu 属性规定元素的上下文菜单。当用户右键点击元素时,会出现上下文菜单。
contextmenu 属性的值是要打开的<menu>元素的 id。
在这里,我注意到浏览器支持问题:

!!!但是!

在Firefox中并没有出现预想中的结果!
然后我发现下面这样可以实现:

所以这里要注意一下,

①w3cschool上<menu>的子标签用的是<command>,是有问题的;菜鸟教程上用的是<menuitem>,亲测可行。

②w3cschool上没有给<menu>加一个type属性,必须要加上type=”context”才能成功实现。

更细致的关于menu和menuitem实现上下文菜单可以参考博文《利用HTML 5中的Menu和Menuitem元素快速创建菜单》

可能大家也发现了,这个contextmenu属性只能在原有的上下文菜单上添加,而不能对原有的上下文菜单项进行删除等操作,这样是它的局限性。这时,就需要我们自己通过js来模拟右键点击出现上下文菜单。

写过一段时间JS的都知道,我们实现这个上下文菜单大概这个思路:
写个ul列表(隐藏掉) -> 监听鼠标右键事件 -> 将ul列表显示出来

但是,实现的时候,还需要注意几个细节,于是我将这个粗糙的思路细化了一下:

(文中js代码都是基于jQuery的)

1.1 阻止浏览器默认右键菜单(上下文菜单)

$(document).bind("contextmenu", function(){ return false; });

1.2 定义一个ul列表

<div style="display:none">
<ul>
<li>menuitem1</li>
<li>menuitem2</li>
<li>menuitem3</li>
</ul>
</div>

这里要注意列表的放置位置,跟上下文菜单作用的区域相关。

1.3 监听鼠标右键点击事件

这里是根据事件对象的button值来判断的,比如mousedown的事件对象:

由于click事件和mousedown事件很像,在这里将两者做一下比较:

鼠标事件 button值 事件描述
click 0 单击鼠标左键
click 1 单击鼠标中键
mousedown 0 鼠标左键被按下
mousedown 1 鼠标中键被按下
mousedown 2 鼠标右键被按下

click事件只会在单击鼠标左键和中键时触发,单击右键时不会触发

给ul列表定位及显示

之前我们提到列表的放置位置,就是为了更准确的给它定位。一般上下文菜单是显示在鼠标稍微右下角的地方,所有首先要定位当前鼠标的坐标。

参数 说明 兼容性
clientX、clientY 触发点相对浏览器可视区域左上角距离,不随页面滚动而改变 所有浏览器均支持
pageX、pageY 触发点相对文档区域左上角距离,会随着页面滚动而改变 IE6/7/8不支持
offsetX、offsetY 触发点相对被触发dom的左上角距离,不过左上角基准点在不同浏览器中有区别,其中在IE中以内容区左上角为基准点不包括边框,如果触发点在边框上会返回负值,而chrome中以边框左上角为基准点。 IE所有版本,chrome,Safari均完美支持,Firefox不支持
layerX、layerY 触发点相对被触发dom左上角的距离,数值与offsetX/Y相同,这个变量就是firefox用来替代offsetX/Y的,基准点为边框左上角,但是有个条件就是,被触发的dom需要设置为position:relative或者position:absolute,否则会返回相对html文档区域左上角的距离 IE6/7/8不支持,opera不支持,IE9/10和Chrome、Safari均支持
screenX、screenY 触发点相对显示器屏幕左上角的距离,不随页面滚动而改变 所有浏览器均支持


选择给鼠标定位的参数,那么就选择相应的方式给ul列表定位。

个人推荐使用pageX和pageY,因为我们最后给ul列表定位时,会用到position:absolute。绝对定位是相对于 static定位以外的第一个父元素进行定位。如果两者都是body定位的话,不容易出现定位失误。然后再display:block

多定义一个事件:鼠标左键点击页面,ul列表display:none

1.4 简单实现

代码:

<body>
<div class="container">
<div class="header">自定义右键菜单-example1</div>
<h1>自定义右键菜单</h1>
<div class="btns">
<button>example-1</button>
<button>example-2</button>
<button>example-3</button>
<button>example-4</button>
</div>
</div>
<div id="contextMenu" style="display:none">
<ul>
<li><a href="#">菜单一</a></li>
<li><a href="#">菜单二</a></li>
<li><a href="#">菜单三</a></li>
</ul>
</div>
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.0.js"></script>
<script type="text/javascript">
$(".btns button").mousedown(function(params){
if(params.button == 2){
$(document).bind('contextmenu',function(){return false;});
$("#contextMenu").css({'top':params.pageY+'px','left':params.pageX+'px'});
$("#contextMenu").show();
}
});
$("body").click(function(){
$("#contextMenu").hide();
})
</script>
</body>

效果:

二、插件

事实上,大神早已为我们打造好了“神器”,请戳张鑫旭的
《jQuery smartMenu右键自定义上下文菜单插件》