基于jQuery的下拉菜单插件
基于jQuery的下拉菜单插件,诸位上眼!!!
前言
很久没有写博客了,话说真的工作后才发现很多需要学的,有很多不足。
加之最近工作没有什么沉淀,现在团队又面临解散,反正闲着也是闲着,就自己写了个插件,反正水平就这样,当时自我总结吧!
应用背景在我们工作中,经常会遇到这种需求:
① 鼠标点击某个文本框时出现下拉菜单
② 常用的操作鼠标划上出现下拉菜单
③ 按钮类应用
我们会用到这种功能往往原因是因为地方小了,按钮多了, 这往往说明产品设计一般出问题了 。。。 但是,我辈屁民豪不关注产品(没资格插手),所以需要完成以上功能;
其实总的来说,这些功能还是非常实用的。
于是,为了应对以上场景,我工作中先是做了一个,然后又遇到了,然后又遇到了,所以最后就写了这么一个东西。
集中展示
几个功能放到一起了,前端代码如下:
View Code
1 <! DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://HdhCmsTestw3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" >
2 < html xmlns ="http://HdhCmsTestw3.org/1999/xhtml" >
3 < head >
4 < title ></ title >
5 < style type ="text/css" >
6 body { font : 12px/1.231 tahoma,arial,宋体 ; }
7 .drop_list_items , .drop_list_items ul { display : none ; position : absolute ; background-color : #FFFFFF ; border : 1px solid #D2D2D2 ; padding : 2px ; margin : 0 ; }
8 .drop_list_items li { margin : 0 ; padding : 4px ; list-style : none ; cursor : pointer ; }
9 .drop_list_items li:hover { background-color : #3399FF ; }
10 .drop_list_items li.parent_drop_list { padding : 4px ; list-style : none ; }
11 .drop_list_items li.cur_active { background-color : #3399FF ; }
12 .z800 { z-index : 800 ; }
13 </ style >
14 < script type ="text/javascript" src ="http://HdhCmsTestcnblogs测试数据/jquery-1.7.1.min.js" ></ script >
15 < script src ="DropList.js" type ="text/javascript" ></ script >
16 < script type ="text/javascript" >
17
18 // 方案一
19 $(document).ready( function () {
20 new DropList({
21 id: ' click_btn_drop ' ,
22 dropItems: [
23 [ ' 短信选定用户 ' , ' select ' ],
24 [ ' 短信全部用户 ' , ' all ' ],
25 [ ' 短信未发送用户用户 ' , ' all_else ' ]
26 ],
27 func: function (e, scope, listEl) {
28 var el = $( this );
29 alert(el.html());
30 scope.closeList();
31 var s = '' ;
32 }
33 });
34
35 new DropList({
36 id: ' div1 ' ,
37 open: ' 1 ' ,
38 close: ' 1 ' ,
39 dropItems: [
40 [ ' 昵称 ' ],
41 [ ' 姓名 ' ],
42 [ ' 性别 ' ],
43 [ ' 联系方式 ' ]
44 ],
45 func: function (e, scope, listEl, toggleEl) {
46 var el = $( this );
47 scope.closeList();
48 toggleEl.val(el.html());
49 }
50 });
51
52 new DropList({
53 id: ' click_text_drop ' ,
54 dropItems: [
55 [ ' 昵称 ' ],
56 [ ' 姓名 ' ],
57 [ ' 性别 ' ],
58 [ ' 联系方式 ' ]
59 ],
60 func: function (e, scope, listEl, toggleEl) {
61 var el = $( this );
62 scope.closeList();
63 toggleEl.val(el.html());
64 }
65 });
66
67 });
68 </ script >
69 </ head >
70 < body >
71
72
73 < div id ="click_btn_drop" style =" 140px;" >
74 点击按钮出现下拉菜单
75 </ div >
76 < br />
77 < br />
78
79
80 < input id ="click_text_drop" type ="text" />
81 < br />
82 < br />
83 < div id ="div1" style =" 140px;" >
84 鼠标滑动
85 </ div >
86
87 </ body >
88 </ html >
js代码:
View Code
var DropList = function (opts) {
if (! opts.id) {
alert( '请指定触发展开事件的元素id' );
return false ;
}
// 触发展开元素id
this .toggleId = opts.id;
this .toggleEl = opts.id ? $('#' + opts.id) : $('body' );
this .key = opts.id ? opts.id + '_list' : new Date().getTime();
this .open = opts.open || 'click'; // 展开菜单方式 mousein
this .close = opts.close || 'click'; // 关闭菜单方式 mouseleave
// this.initShow = false; //判断是否初始化出现菜单绑定事件
/* 下拉菜单数据,可能出现多级菜单数据格式:
[['v', 'k', []], ['v', {}, []],
['v', 'k', [['v', 'k', []], ['v', 'k', []]]
]
*/
this .dropItems = opts.dropItems || null ;
this .loadData = opts.loadData; // 用于异步加载下拉菜单数据//具有层级关系
this .listEl = null ;
this .func = opts.func || null ; // 点击时的事件处理
// 同步方式加载
if ( this .dropItems) {
this .initDropItems();
this .eventBind();
} else {
}
};
DropList.prototype.closeList = {};
DropList.prototype.dropItemLoad = function (data, el) {
for ( var i in data) {
var item = data[i];
var tmp = $('<li></li>' );
el.append(tmp); // 标签已装载
if (item[0 ]) {
tmp.html(item[ 0 ]);
}
if (item[1] || typeof item[1] == 'number' ) {
if ( typeof item[1] == 'string' || typeof item[1] == 'number' ) {
tmp.attr( 'id', item[1 ]);
} else {
for (_k in item[1 ]) {
tmp.attr(_k, item[ 1 ][_k]);
}
}
}
if (item[2] && item[2]['length']) { // 此处需要递归
var child = $('<ul ></ul>' )
tmp.append(child);
tmp.addClass( 'parent_drop_list' );
this .dropItemLoad(item[2 ], child);
}
}
};
// ['v', 'k', []]
DropList.prototype.initDropItems = function () {
var scope = this ;
var dropItems = scope.dropItems;
var listEl = $('<ul class="drop_list_items" id="' + scope.key + '"></ul>' );
$( 'body' ).append(listEl);
scope.dropItemLoad(dropItems, listEl);
scope.listEl = listEl;
};
DropList.prototype.closeList = function () {
var listEl = this .listEl;
listEl.find( 'li').removeClass('cur_active' );
listEl.find( 'ul' ).hide();
listEl.hide();
};
DropList.prototype.eventBind = function () {
var scope = this ;
var listEl = scope.listEl;
var toggleEl = scope.toggleEl;
var open = scope.open;
var close = scope.close;
var func = scope.func;
var obj_len = function (o) {
var len = 0 ;
for ( var k in o) {
len ++ ;
}
return len;
};
var func_cls = function () {
if (close == 'click' ) {
$(document).click( function (e) {
var el = $(e.target);
var is_el = false ;
// 判断父元素是否为
while (el.attr('id') != scope.key) {
if (el.is("ul") || el.is('li' )) {
is_el = true ;
el = el.parent();
} else {
break ;
}
}
if (el.attr('id') == scope.toggleId) {
is_el = true ;
}
if (! is_el) {
scope.closeList();
if (scope.closeList[scope.toggleId])
delete scope.closeList[scope.toggleId];
if (obj_len(scope.closeList) == 0 )
$(document).unbind( 'click' );
var s = '' ;
}
});
} else {
listEl.mouseleave( function (e) {
scope.closeList();
if (scope.closeList[scope.toggleId])
delete scope.closeList[scope.toggleId];
listEl.unbind( 'mouseleave' );
});
}
};
// 确认弹出层位置
var func_init_pos = function (el) {
var offset = el.offset();
var h = el.height();
var p_top = el.css('padding-top' );
var p_bottom = el.css('padding-bottom' );
listEl.css( 'min-width', (parseInt(el.css('width')) + parseInt(el.css('padding-left')) + parseInt(el.css('padding-right')) - 6) + 'px' )
listEl.css( 'left', parseInt(offset.left) + 'px' );
listEl.css( 'top', (parseInt(offset.top) + parseInt(h) + parseInt(p_top) + parseInt(p_bottom)) + 'px' );
};
if (open == 'click' ) {
toggleEl.unbind( 'click').click( function (e) {
var el = $( this );
var drop_list_items = $('.drop_list_items' );
func_init_pos(el);
drop_list_items.removeClass( 'z800' );
listEl.addClass( 'z800' );
listEl.show();
func_cls();
scope.closeList[scope.toggleId] = 1 ;
// e.stopPropagation(); //阻止冒泡
});
} else {
toggleEl.unbind( 'mouseenter').mouseenter( function (e) {
var el = $( this );
var drop_list_items = $('.drop_list_items' );
func_init_pos(el);
drop_list_items.removeClass( 'z800' );
listEl.addClass( 'z800' );
listEl.show();
func_cls();
// e.stopPropagation(); //阻止冒泡
});
}
listEl.delegate( 'li', 'mouseenter', function (e) {
var el = $( this );
listEl.find( 'li').removeClass('cur_active' );
listEl.find( 'ul' ).hide();
el.addClass( 'cur_active' );
el.children().show();
el = el.parent();
while (el.attr('id') != scope.key) {
if (el.is("li" )) {
el.addClass( 'cur_active' );
}
if (el.is('ul' )) {
el.show();
}
el = el.parent();
}
e.stopPropagation();
});
if (func && typeof func == 'function' ) {
listEl.delegate( 'li', 'click', function (e) {
func.call( this , e, scope, listEl, toggleEl);
e.stopPropagation();
});
}
};
function initNewDrop(opts) {
new DropList(opts);
}
难点&后续
做的过程中还是遇到了几个问题的,比如:
① 菜单展开后如何关闭
② 多级菜单如何处理
不完善的级联效果
③ 事件如何回调
最后做出了这个比较简陋的东东。。。。
但是做完后也发现了一些问题:
① 像这种菜单在最左最下出现时没有解决;
② 然后菜单项过多时候也没有出现像select的滚动条;
③ 由于个人水平,整个代码的质量亦有问题;
④ 开始也考虑了异步数据加载的问题,但是着实有点难便放弃了,功能代码有一点,有兴趣的同学可以看看:
View Code
1 <! DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://HdhCmsTestw3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" >
2 < html xmlns ="http://HdhCmsTestw3.org/1999/xhtml" >
3 < head >
4 < title ></ title >
5 < style type ="text/css" >
6 *
7 {
8 margin : 0 ;
9 }
10 body
11 {
12 font : 12px/1.231 tahoma,arial,宋体 ;
13 }
14 div
15 {
16 width : 160px ;
17 margin : 5px ;
18 }
19
20 .drop_btn ul
21 {
22 position : absolute ;
23 background-color : #FFFFFF ;
24 border : 1px solid #D2D2D2 ;
25 padding : 2px ;
26 line-height : 18px ;
27 display : none ;
28 top : 25px ;
29 z-index : 500 ;
30
31 }
32
33 .drop_btn li
34 {
35 list-style : none ;
36 }
37 .drop_btn_toggle
38 {
39 background : url("http://shz.qq测试数据/statics/images/button.png") repeat-x scroll 0 0 #E5E5E5 ;
40 border : 1px solid #999999 ;
41 box-shadow : 0 1px 0 #E5E5E5 ;
42 border-radius : 3px ;
43 cursor : pointer ;
44 height : 28px ;
45 line-height : 28px ;
46 *height : 18px ;
47 *line-height : 18px ;
48 padding : 3px 6px ;
49 vertical-align : middle ;
50 zoom : 1 ;
51 }
52 .drop_btn_open .drop_btn_toggle
53 {
54 background : url("http://shz.qq测试数据/statics/images/button_selected.png") repeat-x scroll 0 0 #B4B4B4 ;
55 border-color : #CCCCCC #B1B1B1 #AFAFAF #BEBEBE ;
56 color : #515151 ;
57 }
58 .drop_btn_toggle .icon
59 {
60 border-left : 4px dashed transparent ;
61 border-right : 4px dashed transparent ;
62 border-top : 4px solid ;
63 display : inline-block ;
64 width : 0 ;
65 height : 0 ;
66 margin : 11px 0 0 4px ;
67 *margin : 6px 0 0 4px ;
68 overflow : hidden ;
69 vertical-align : top ;
70 }
71
72 div.drop_btn_open ul
73 {
74 display : block ;
75 }
76 .drop_btn li
77 {
78 padding : 2px ;
79 cursor : pointer ;
80 }
81 .drop_btn li:hover
82 {
83 background-color : #3399FF ;
84 }
85 </ style >
86 < script type ="text/javascript" src ="http://HdhCmsTestcnblogs测试数据/jquery-1.7.1.min.js" ></ script >
87 < script src ="DropList.js" type ="text/javascript" ></ script >
88 < script type ="text/javascript" >
89
90 // 方案一
91 $(document).ready( function () {
92 var click = new DropList({
93 id: ' click_btn_drop ' ,
94 toggleText: ' 给用户发送短信 ' ,
95 openType: ' move ' ,
96 drop_items: [
97 { id: ' select ' , text: ' 短信选定用户 ' },
98 { id: ' all ' , text: ' 短信全部用户 ' },
99 { id: ' all_else ' , text: ' 短信未发送用户用户 ' }
100 ],
101 func: function (e, container) {
102 var el = $( this );
103 alert(el.html())
104 var s = '' ;
105 }
106 });
107
108 var move = new DropList({
109 id: ' move_btn_drop ' ,
110 toggleText: ' 给用户发送短信 ' ,
111
112 loadData: function (callBack) {
113 var scope = this ;
114 $.get( ' Handler.ashx ' , function (data) {
115 if (data && typeof data == ' string ' ) {
116 data = eval( ' ( ' + data + ' ) ' );
117 }
118 data = data.data;
119 var param = [];
120 param.push({ text: ' 报名人数: ' + data.reg_num });
121 var type = data.notice;
122 if (type == 0 ) {
123 msg = ' 不发送短信 ' ;
124 } else if (type == 1 ) {
125 msg = ' 自动短信 ' ;
126 } else if (type == 3 ) {
127 msg = ' 手动短信 ' ;
128 }
129 param.push({ text: ' 短信类型: ' + msg });
130 param.push({ text: ' <a href="#">自动短信条数: ' + data.sms_auto_count + ' </a> ' });
131 param.push({ text: ' <a href="http://HdhCmsTestbaidu测试数据/" target="_blank">手动短信条数: ' + data.sms_manual_count });
132
133 scope.drop_items = param;
134 callBack();
135
136 var s = '' ;
137
138 });
139 }
140 });
141
142 var text = new DropList({
143 id: ' click_text_drop ' ,
144 toggleType: ' text ' ,
145 drop_items: [
146 { text: ' 昵称 ' },
147 { text: ' 姓名 ' },
148 { text: ' 性别 ' },
149 { text: ' 联系方式 ' }
150 ],
151 func: function (e, container, toggleEl) {
152 var el = $( this );
153 toggleEl.val(el.html());
154 }
155 });
156
157 });
158 </ script >
159 </ head >
160 < body >
161
162 点击按钮出现下拉菜单
163 < div id ="click_btn_drop" class ="drop_btn" ></ div >
164 < br />
165 < br />
166
167 滑动按钮出现下拉菜单
168 < div id ="move_btn_drop" class ="drop_btn" ></ div >
169 < br />
170 < br />
171
172 点击文本出现下拉菜单
173 < div id ="click_text_drop" class ="drop_btn" ></ div >
174 < br />
175 < br />
176
177 <!-- <div class="drop_btn">
178 <a class="drop_btn_toggle"><span>给用户发短信<i class="icon"></i></span></a>
179 <ul class="drop_items">
180 <li>短信选中用户</li>
181 <li>短信全部用户</li>
182 <li>短信未发送用户</li>
183 </ul>
184 </div> -->
185
186 </ body >
187 </ html >
188
189 /// < reference path ="http://HdhCmsTestcnblogs测试数据/jquery-1.7.1.min.js" />
190
191
192 var DropList = function (opts) {
193 this.id = opts.id || '';
194 //组件容器
195 this.container = $('#' + this.id);
196 //确定点击/滑动元素为按钮或者文本框(button/text)
197 this.toggleType = opts.toggleType || 'button';
198 this.toggleText = opts.toggleText || '请点击我';
199 //展开方式(点击/滑动)
200 this.openType = opts.openType || 'click';
201 this.drop_items = opts.drop_items || [];
202 this.loadData = opts.loadData;
203 this.func = opts.func;
204
205 if (this.drop_items && this.drop_items[0]) {
206 this.init();
207 this.eventBind();
208 } else {
209 if (this.loadData && typeof this.loadData == 'function') {
210 this.asyncLoad();
211 }
212 }
213 };
214
215 DropList.prototype.initBtn = function () {
216 var scope = this;
217 var container = scope.container;
218 var openType = scope.openType;
219 var toggleType = scope.toggleType;
220 var toggleText = scope.toggleText;
221 var toggleEl = '';
222 if (toggleType == 'button') {
223 toggleEl = ' < a class ="drop_btn_toggle" >< span > ' + toggleText + ' < i class ="icon" ></ i ></ span ></ a > ';
224 } else {
225 toggleEl = ' < input class ="drop_text_toggle" type ="text" /> ';
226 }
227 //获得点击元素用以添加事件
228 scope.toggleEl = $(toggleEl);
229 container.append(scope.toggleEl);
230 };
231
232 DropList.prototype.initDropItems = function () {
233 var scope = this;
234 var container = scope.container;
235 //组装下拉元素
236 var drop_items = scope.drop_items;
237 var item_container = $(' < ul class ="drop_items" ></ ul > ');
238 container.append(item_container);
239 for (var i in drop_items) {
240 var item = drop_items[i];
241 var tmp = $(' < li ></ li > ');
242 if (item.id) {
243 tmp.attr('id', item.id);
244 }
245 if (item.text) {
246 tmp.html(item.text);
247 }
248 item_container.append(tmp);
249 }
250 //活动下拉菜单
251 scope.item_container = item_container;
252 };
253 DropList.prototype.init = function () {
254 //组装触发元素
255 var scope = this;
256 scope.initBtn();
257 scope.initDropItems();
258 };
259
260 DropList.prototype.eventBind = function () {
261 var scope = this;
262 var container = scope.container; //父容器
263 var toggleType = scope.toggleType; //触发方式
264 var toggleEl = scope.toggleEl; //点击元素
265 var item_container = scope.item_container; //下拉菜单
266 var openType = scope.openType;
267 var func = scope.func;
268
269 if (openType == 'click') {
270 toggleEl.click(function (e) {
271 container.addClass('drop_btn_open');
272
273 var el = $(this);
274 var offset = el.offset();
275 var s = el.height();
276 s = el.css('height');
277 var p_top = el.css('padding-top');
278 var p_bottom = el.css('padding-bottom');
279 item_container.css('min-width', el.css('width'))
280 item_container.css('left', parseInt(offset.left) + 'px');
281 item_container.css('top', (parseInt(offset.top) + parseInt(s) + parseInt(p_top) + parseInt(p_bottom)) + 'px');
282
283
284 //菜单出现后便阻止冒泡
285 e.stopPropagation();
286 $(document).unbind('click').click(function (ee) {
287 if (container.hasClass('drop_btn_open')) {
288 $('.drop_btn').removeClass('drop_btn_open');
289 }
290 $(document).unbind('click');
291 });
292 });
293 } else {
294 toggleEl.mousemove(function () {
295 container.addClass('drop_btn_open');
296
297 var el = $(this);
298 var offset = el.offset();
299 var s = el.height();
300 s = el.css('height');
301 var p_top = el.css('padding-top');
302 var p_bottom = el.css('padding-bottom');
303 item_container.css('min-width', el.css('width'))
304 item_container.css('left', parseInt(offset.left) + 'px');
305 item_container.css('top', (parseInt(offset.top) + parseInt(s) + parseInt(p_top) + parseInt(p_bottom)) + 'px');
306
307 $(document).unbind('click').click(function (ee) {
308 if (container.hasClass('drop_btn_open')) {
309 $('.drop_btn').removeClass('drop_btn_open');
310 }
311 $(document).unbind('click');
312 });
313 });
314 }
315
316 // $("table").delegate("td", "hover", function () {
317 // $(this).toggleClass("hover");
318 // });
319 //处理选项处理事件
320 if (func && typeof func == 'function') {
321 item_container.delegate('li', 'click', function (e) {
322 func.call(this, e, container, toggleEl);
323 });
324 }
325 };
326
327 DropList.prototype.asyncLoad = function () {
328 var scope = this;
329 scope.initBtn();
330 scope.asyncBtnEventBind();
331
332 // scope.initDropItems();
333 };
334
335 DropList.prototype.asyncBtnEventBind = function () {
336 var scope = this;
337 var container = scope.container; //父容器
338 var toggleType = scope.toggleType; //触发方式
339 var toggleEl = scope.toggleEl; //点击元素
340 var loadData = scope.loadData;
341 var openType = scope.openType;
342
343 //处理点击事件
344 if (openType == 'click') {
345 toggleEl.click(function (e) {
346 container.addClass('drop_btn_open');
347
348 var el = $(this);
349
350
351 //若是没有下拉菜单便添加数据
352 if (!scope.item_container) {
353 //加载异步数据
354 if (loadData && typeof loadData == 'function') {
355 loadData.call(scope, function () {
356 scope.initDropItems();
357 scope.asyncDropEventBind(el);
358 });
359 }
360 }
361
362 //菜单出现后便阻止冒泡
363 e.stopPropagation();
364 $(document).unbind('click').click(function (ee) {
365 if (container.hasClass('drop_btn_open')) {
366 $('.drop_btn').removeClass('drop_btn_open');
367 }
368 $(document).unbind('click');
369 });
370 });
371 } else {
372 toggleEl.mousemove(function () {
373 if (container.hasClass('drop_btn_open')) {
374 $('.drop_btn').removeClass('drop_btn_open');
375 }
376 $(document).unbind('click');
377 });
378 }
379 };
380
381 DropList.prototype.asyncDropEventBind = function (el) {
382 var scope = this;
383 var item_container = scope.item_container; //下拉菜单
384
385 var offset = el.offset();
386 var s = el.height();
387 s = el.css('height');
388 var p_top = el.css('padding-top');
389 var p_bottom = el.css('padding-bottom');
390 item_container.css('min-width', el.css('width'))
391 item_container.css('left', parseInt(offset.left) + 'px');
392 item_container.css('top', (parseInt(offset.top) + parseInt(s) + parseInt(p_top) + parseInt(p_bottom)) + 'px');
393
394 var func = scope.func;
395
396 // $("table").delegate("td", "hover", function () {
397 // $(this).toggleClass("hover");
398 // });
399 //处理选项处理事件
400 if (func && typeof func == 'function') {
401 item_container.delegate('li', 'click', function (e) {
402 func.call(this, e, container, toggleEl);
403 });
404 }
405 };
所以先贴出来和各位看看,后续小生再行优化,希望能把这个功能做好!
分类: Web前端
作者: Leo_wl
出处: http://HdhCmsTestcnblogs测试数据/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息