今天是給你們介紹一款在網頁上使用的右鍵菜單,原做者的網址是: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