前言
很久没有写博客了,话说真的工作后才发现很多需要学的,有很多不足。
加之最近工作没有什么沉淀,现在团队又面临解散,反正闲着也是闲着,就自己写了个插件,反正水平就这样,当时自我总结吧!
应用背景
在我们工作中,经常会遇到这种需求:
① 鼠标点击某个文本框时出现下拉菜单
② 常用的操作鼠标划上出现下拉菜单
③ 按钮类应用
我们会用到这种功能往往原因是因为地方小了,按钮多了,这往往说明产品设计一般出问题了。。。 但是,我辈屁民豪不关注产品(没资格插手),所以需要完成以上功能;
其实总的来说,这些功能还是非常实用的。
于是,为了应对以上场景,我工作中先是做了一个,然后又遇到了,然后又遇到了,所以最后就写了这么一个东西。
集中展示
几个功能放到一起了,前端代码如下:
View Code
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 2 <html xmlns="http://www.w3.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://www.cnblogs.com/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=" width: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=" width: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://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2 <html xmlns="http://www.w3.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.com/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.com/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