0x00 【一般思路】
严格来讲,右键菜单应该叫上下文菜单,在html5中也新定义了这个contextmenu全局属性。
它的语法是这样的:1
2
3
4
5<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 | $(document).bind("contextmenu", function(){ return false; }); |
定义一个ul列表
1 | <div style="display:none"> |
这里要注意列表的放置位置,跟上下文菜单作用的区域相关。
监听鼠标右键点击事件
这里是根据事件对象的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
0x01 【简单实现】
代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34<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>
效果:
0x02 【插件】
事实上,大神早已为我们打造好了“神器”,请戳张鑫旭的
《jQuery smartMenu右键自定义上下文菜单插件》
本文完