今天是给你们介绍一款在网页上使用的右键菜单,原做者的网址是:http://51jsr.javaeye.com/blog/305517javascript
这个右键菜单已经很是优秀,不过呢。倒是IE Only,并且在DTD模式下
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd%22>css
连IE显示都是有问题的,因此呢只有本身动手了,另外就顺便改形成jQuery控件,顺便分析一下代码。html
首先来看一下效果吧java
↑这是控件的效果数组
插一句吧,其实我最终的目标是提供一个ASP.NET MVC 框架前台UI Controls解决方案,由于后面的控件会用到这个右键菜单因此就讲一下。框架
首先仍是来分析一下HTML吧dom
1:一级菜单(每一组菜单)便是一个独立的div容器ide
2:每一项又是div,嵌套一个nobr(可用div代替不过要额外写个class)的标签,里面是图标和span包裹的位置内容函数
这里一个要注意的地方就是多级菜单其实在HTMl结构是分离的,只是经过显示的位置在视觉上给人连载一块儿(另外就是箭头图标了)
第二接着是CSS了(是修改过的)
CSS很是简单,由于HTML结构自己也不复杂
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
.b-m-mpanel {
background: #F0F0F0 url(images/contextmenu/menu_bg.gif) left repeat-y;
border: 1px solid #718BB7;
padding: 2px 0;
position: absolute;
z-index: 99997;
}
.b-m-split {
height: 6px;
background: url(images/contextmenu/m_splitLine.gif) center repeat-x;
font-size: 0px;
margin: 0 2px;
}
.b-m-item, .b-m-idisable
{
padding: 4px 2px;
margin: 0 2px 0 3px;
cursor: normal;
line-height:100%;
}
.b-m-idisable
{
color:#808080;
}
.b-m-ibody, .b-m-arrow {
overflow: hidden;
text-overflow: ellipsis;
}
.b-m-arrow {
background: url(images/contextmenu/m_arrow.gif) right no-repeat;
}
.b-m-idisable .b-m-arrow
{
background:none;
}
.b-m-item img, .b-m-ifocus img, .b-m-idisable img {
margin-right: 8px;
}
.b-m-ifocus {
background: url(images/contextmenu/m_item.gif) repeat-x bottom;
border: 1px solid #AACCF6;
padding: 3px 1px ;
margin: 0 2px 0 3px;
cursor: normal;
line-height:100%;
}
.b-m-idisable img {
visibility:hidden;
}
|
第三来看javascript了
先来看个完整的吧
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
|
;(
function
($) {
function
returnfalse() {
return
false
; };
$.fn.contextmenu =
function
(option) {
option = $.extend({ alias:
"cmroot"
, width: 150 }, option);
var
ruleName =
null
, target =
null
,
groups = {}, mitems = {}, actions = {}, showGroups = [],
itemTpl =
"<div class='b-m-$[type]' unselectable=on><nobr unselectable=on><img src='$[icon]' align='absmiddle'/><span unselectable=on>$[text]</span></nobr></div>"
;
var
gTemplet = $(
"<div/>"
).addClass(
"b-m-mpanel"
).attr(
"unselectable"
,
"on"
).css(
"display"
,
"none"
);
var
iTemplet = $(
"<div/>"
).addClass(
"b-m-item"
).attr(
"unselectable"
,
"on"
);
var
sTemplet = $(
"<div/>"
).addClass(
"b-m-split"
);
//建立菜单组
var
buildGroup =
function
(obj) {
groups[obj.alias] =
this
;
this
.gidx = obj.alias;
this
.id = obj.alias;
if
(obj.disable) {
this
.disable = obj.disable;
this
.className =
"b-m-idisable"
;
}
$(
this
).width(obj.width).click(returnfalse).mousedown(returnfalse).appendTo($(
"body"
));
obj =
null
;
return
this
;
};
var
buildItem =
function
(obj) {
var
T =
this
;
T.title = obj.text;
T.idx = obj.alias;
T.gidx = obj.gidx;
T.data = obj;
T.innerHTML = itemTpl.replace(/\$\[([^\]]+)\]/g,
function
() {
return
obj[arguments[1]];
});
if
(obj.disable) {
T.disable = obj.disable;
T.className =
"b-m-idisable"
;
}
obj.items && (T.group =
true
);
obj.action && (actions[obj.alias] = obj.action);
mitems[obj.alias] = T;
T = obj =
null
;
return
this
;
};
//添加菜单项
var
addItems =
function
(gidx, items) {
var
tmp =
null
;
for
(
var
i = 0; i < items.length; i++) {
if
(items[i].type ==
"splitLine"
) {
//菜单分隔线
tmp = sTemplet.clone()[0];
}
else
{
items[i].gidx = gidx;
if
(items[i].type ==
"group"
) {
//菜单组
buildGroup.apply(gTemplet.clone()[0], [items[i]]);
arguments.callee(items[i].alias, items[i].items);
items[i].type =
"arrow"
;
tmp = buildItem.apply(iTemplet.clone()[0], [items[i]]);
}
else
{
//菜单项
items[i].type =
"ibody"
;
tmp = buildItem.apply(iTemplet.clone()[0], [items[i]]);
$(tmp).click(
function
(e) {
if
(!
this
.disable) {
if
($.isFunction(actions[
this
.idx])) {
actions[
this
.idx].call(
this
, target);
}
hideMenuPane();
}
return
false
;
});
}
//Endif
$(tmp).bind(
"contextmenu"
, returnfalse).hover(overItem, outItem);
}
//Endif
groups[gidx].appendChild(tmp);
tmp = items[i] = items[i].items =
null
;
}
//Endfor
gidx = items =
null
;
};
var
overItem =
function
(e) {
//若是菜单项不可用
if
(
this
.disable)
return
false
;
hideMenuPane.call(groups[
this
.gidx]);
//若是是菜单组
if
(
this
.group) {
var
pos = $(
this
).offset();
var
width = $(
this
).outerWidth();
showMenuGroup.apply(groups[
this
.idx], [pos, width]);
}
this
.className =
"b-m-ifocus"
;
return
false
;
};
//菜单项失去焦点
var
outItem =
function
(e) {
//若是菜单项不可用
if
(
this
.disable )
return
false
;
if
(!
this
.group) {
//菜单项
this
.className =
"b-m-item"
;
}
//Endif
return
false
;
};
//在指定位置显示指定的菜单组
var
showMenuGroup =
function
(pos, width) {
var
bwidth = $(
"body"
).width();
var
bheight = document.documentElement.clientHeight;
var
mwidth = $(
this
).outerWidth();
var
mheight = $(
this
).outerHeight();
pos.left = (pos.left + width + mwidth > bwidth) ? (pos.left - mwidth < 0 ? 0 : pos.left - mwidth) : pos.left + width;
pos.top = (pos.top + mheight > bheight) ? (pos.top - mheight + (width > 0 ? 25 : 0) < 0 ? 0 : pos.top - mheight + (width > 0 ? 25 : 0)) : pos.top;
$(
this
).css(pos).show();
showGroups.push(
this
.gidx);
};
//隐藏菜单组
var
hideMenuPane =
function
() {
var
alias =
null
;
for
(
var
i = showGroups.length - 1; i >= 0; i--) {
if
(showGroups[i] ==
this
.gidx)
break
;
alias = showGroups.pop();
groups[alias].style.display =
"none"
;
mitems[alias] && (mitems[alias].className =
"b-m-item"
);
}
//Endfor
//CollectGarbage();
};
function
applyRule(rule) {
if
(ruleName && ruleName == rule.name)
return
false
;
for
(
var
i
in
mitems)
disable(i, !rule.disable);
for
(
var
i = 0; i < rule.items.length; i++)
disable(rule.items[i], rule.disable);
ruleName = rule.name;
};
function
disable(alias, disabled) {
var
item = mitems[alias];
item.className = (item.disable = item.lastChild.disabled = disabled) ?
"b-m-idisable"
:
"b-m-item"
;
};
/** 右键菜单显示 */
function
showMenu(e, menutarget) {
target = menutarget;
showMenuGroup.call(groups.cmroot, { left: e.pageX, top: e.pageY }, 0);
$(document).one(
'mousedown'
, hideMenuPane);
}
var
$root = $(
"#"
+ option.alias);
var
root =
null
;
if
($root.length == 0) {
root = buildGroup.apply(gTemplet.clone()[0], [option]);
root.applyrule = applyRule;
root.showMenu = showMenu;
addItems(option.alias, option.items);
}
else
{
root = $root[0];
}
var
me = $(
this
).each(
function
() {
return
$(
this
).bind(
'contextmenu'
,
function
(e) {
var
bShowContext = (option.onContextMenu && $.isFunction(option.onContextMenu)) ? option.onContextMenu.call(
this
, e) :
true
;
if
(bShowContext) {
if
(option.onShow && $.isFunction(option.onShow)) {
option.onShow.call(
this
, root);
}
root.showMenu(e,
this
);
}
return
false
;
});
});
//设置显示规则
if
(option.rule) {
applyRule(option.rule);
}
gTemplet = iTemplet = sTemplet = itemTpl = buildGroup = buildItem =
null
;
addItems = overItem = outItem =
null
;
//CollectGarbage();
return
me;
}
})(jQuery);
|
那接着就一步一步来分析呗,首先既然改形成jQuery控件那么天然仍是老架子
1
2
3
4
|
;(
function
($) {
$.fn.contextmenu =
function
(option) {
}
})(jQuery);
|
1
2
3
|
//alias:"惟一标示"(这个标示很重要哦,能够实现屡次调用只生成一个菜单哦),
//width菜单宽度
option = $.extend({ alias:
"cmroot"
, width: 150 }, option);
|
1
2
3
4
5
6
|
/*参数说明
option: {width:Number, items:Array, onShow:Function, rule:JSON}
成员语法(三种形式) -- para.items
-> {text:String, icon:String, type:String, alias:String, width:Number, items:Array} -- 菜单组
-> {text:String, icon:String, type:String, alias:String, action:Function } -- 菜单项
-> {type:String} --分割线*/
|
详细描述下:
items:Array 右键菜单的内容定义,数组的元素格式以下所示:
{text: String, icon: String, alias: String, type: "group"|"item"|"splitLine", width:int, items:Array,action:Funtion}
其中:
text:String 菜单项的文字说明 。
icon: String 图标的Src地址,若是没有图标,若是item不须要图标,请设置成none.gif(在images/icons/中能够找到)。
alias:String 惟一标识菜单项。
type:"group"|"item"|"splitLine" 分别为组,项,分割线,当选择是"splitLine"则其余设置项无需设置。
width:int 当且仅当type="group"时有效,设置新组容器的宽度。
items:Array 子元素可无限层次。
action:Function 当菜单项被点击时被使用
alias: String (可选参数)惟一标识,当页面上只有一种右键菜单时能够省略
width : Number (可选参数) 右键菜单根的宽度, 默认值:150px。
onContextMenu: Function (可选参数) 当右键菜单触发时预先调用的函数,返回参数为Boolean指示是否显示菜单
onShow: Function (可选参数) 当菜单显示时触发,通常在该函数中应用规则
rule : Json (可选参数) 默认规则,设置哪些项默认为禁用,格式以下所示 { name:String, disable: Boolean, items:Array}
name:String 规则名称 disable:Boolean 规则是禁用仍是启用 items:Array 须要应用规则的item alias的集合
有点复杂哈,若是还有不明白看示例哈。
定义一堆临时变量,还有4个模板临时变量
1
2
3
4
5
6
7
|
var
ruleName =
null
, target =
null
,
groups = {}, mitems = {}, actions = {}, showGroups = [],
//定义内部的临时变量。用到的地方再来分析
//一个菜单项的模板哦 ,容器和项,分割线的模板
itemTpl =
"<div class='b-m-$[type]' unselectable=on><nobr unselectable=on><img src='$[icon]' align='absmiddle'/><span unselectable=on>$[text]</span></nobr></div>"
;
var
gTemplet = $(
"<div/>"
).addClass(
"b-m-mpanel"
).attr(
"unselectable"
,
"on"
).css(
"display"
,
"none"
);
var
iTemplet = $(
"<div/>"
).addClass(
"b-m-item"
).attr(
"unselectable"
,
"on"
);
var
sTemplet = $(
"<div/>"
).addClass(
"b-m-split"
);
|
接着咱们要跳过一些函数的定义,直接来看建立HTML的部分
1
2
3
4
5
6
7
8
9
10
11
12
|
//获取菜单的跟
var
$root = $(
"#"
+ option.alias);
var
root =
null
;
if
($root.length == 0) {
//若是顶级不存在,这建立顶级菜单哦
root = buildGroup.apply(gTemplet.clone()[0], [option]);
root.applyrule = applyRule;
//把一个方法注册到dom上
root.showMenu = showMenu;
//另一个方法注册的该dom上
addItems(option.alias, option.items);
//添加菜单项
}
else
{
root = $root[0];
//不然就用这个了
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
var
buildGroup =
function
(obj) {
//建立菜单容器
groups[obj.alias] =
this
;
//菜单项注册到临时变量中
this
.gidx = obj.alias;
this
.id = obj.alias;
if
(obj.disable) {
//若是是禁用状态
this
.disable = obj.disable;
this
.className =
"b-m-idisable"
;
}
//设置菜单宽度,设置事件的阻止事件冒泡,并添加到body中
$(
this
).width(obj.width).click(returnfalse).mousedown(returnfalse).appendTo($(
"body"
));
obj =
null
;
return
this
;
//返回菜单自己
};
|
有了容器就能够往里面添加菜单项了,我在代码中加了详细的注释了,应该能够很好的理解了
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
35
36
37
38
|
//添加菜单项
var
addItems =
function
(gidx, items) {
var
tmp =
null
;
for
(
var
i = 0; i < items.length; i++) {
if
(items[i].type ==
"splitLine"
) {
//若是是分割线
//菜单分隔线
tmp = sTemplet.clone()[0];
}
else
{
items[i].gidx = gidx;
//把group的标识赋给item上
if
(items[i].type ==
"group"
) {
//菜单组
buildGroup.apply(gTemplet.clone()[0], [items[i]]);
//每一个菜单组都是独立的div哦,因此顶级同样调用生产组的方法
arguments.callee(items[i].alias, items[i].items);
//递归生成菜单项
items[i].type =
"arrow"
;
//若是是group生成箭头
tmp = buildItem.apply(iTemplet.clone()[0], [items[i]]);
//生成菜单项的html
}
else
{
//菜单项
items[i].type =
"ibody"
;
tmp = buildItem.apply(iTemplet.clone()[0], [items[i]]);
//生成菜单项的html
$(tmp).click(
function
(e) {
//若是菜单项那么注册click事件
if
(!
this
.disable) {
if
($.isFunction(actions[
this
.idx])) {
actions[
this
.idx].call(
this
, target);
}
hideMenuPane();
}
return
false
;
});
}
//Endif
//把菜单项的右键事件屏蔽,同时注册hover的效果
$(tmp).bind(
"contextmenu"
, returnfalse).hover(overItem, outItem);
}
//Endif
groups[gidx].appendChild(tmp);
//把菜单项添加到group的中
tmp = items[i] = items[i].items =
null
;
}
//Endfor
gidx = items =
null
;
};
|
builditem方法就比较简单,就不详细描述了,接着咱们仍是继续往下看主流程了哦
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
var
me = $(
this
).each(
function
() {
//给元素添加右键事件了哦
return
$(
this
).bind(
'contextmenu'
,
function
(e) {
//若是(option.onContextMenu 存在则调用并判断返回值是否显示菜单,能够利用这个在特定状况下禁用菜单
var
bShowContext = (option.onContextMenu && $.isFunction(option.onContextMenu)) ? option.onContextMenu.call(
this
, e) :
true
;
if
(bShowContext) {
//触发onShow事件,这个事件中能够执行修改rule,禁用某几项菜单项哦
if
(option.onShow && $.isFunction(option.onShow)) {
option.onShow.call(
this
, root);
}
root.showMenu(e,
this
);
//调用显示菜单
}
//阻止冒泡
return
false
;
});
});
//设置显示规则,第一次执行时的规则,同时也能够onshow中动态设置rule
if
(option.rule) {
applyRule(option.rule);
}
|
基本就OK了,另外几个方法就比较简单了,还有亮点是边缘的处理,这个前面的datepicker中也有相应的说明逻辑差很少就不在描述了,一样仍是来看下demo吧。关于打包下载,你们能够把demo的网页完整的另存为便可
http://jscs.cloudapp.net/ControlsSample/CM
你的支持是我继续写做的动力。
欢迎转载,可是请保留原连接:http://www.cnblogs.com/xuanye/archive/2009/10/29/1592585.html