前言
很久没有写博客了,话说真的工作后才发现很多需要学的,有很多不足。
加之最近工作没有什么沉淀,现在团队又面临解散,反正闲着也是闲着,就自己写了个插件,反正水平就这样,当时自我总结吧!
应用背景
在我们工作中,经常会遇到这种需求:
① 鼠标点击某个文本框时出现下拉菜单
② 常用的操作鼠标划上出现下拉菜单
③ 按钮类应用
我们会用到这种功能往往原因是因为地方小了,按钮多了,这往往说明产品设计一般出问题了。。。 但是,我辈屁民豪不关注产品(没资格插手),所以需要完成以上功能;
其实总的来说,这些功能还是非常实用的。
于是,为了应对以上场景,我工作中先是做了一个,然后又遇到了,然后又遇到了,所以最后就写了这么一个东西。
集中展示
几个功能放到一起了,前端代码如下:
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" >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" >84 鼠标滑动85 </div>86 87 </body>88 </html>
js代码:
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的滚动条;
③ 由于个人水平,整个代码的质量亦有问题;
④ 开始也考虑了异步数据加载的问题,但是着实有点难便放弃了,功能代码有一点,有兴趣的同学可以看看:
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 background-color: #3399FF; 84 } 85 </style> 86 <script type="text/javascript" src="http://www.cnblogs.com/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://www.baidu.com/" 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://www.cnblogs.com/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 };
所以先贴出来和各位看看,后续小生再行优化,希望能把这个功能做好!