基于jQuery的下拉菜单插件,诸位上眼!!!

前言


很久没有写博客了,话说真的工作后才发现很多需要学的,有很多不足。

加之最近工作没有什么沉淀,现在团队又面临解散,反正闲着也是闲着,就自己写了个插件,反正水平就这样,当时自我总结吧!

应用背景


在我们工作中,经常会遇到这种需求:

① 鼠标点击某个文本框时出现下拉菜单

② 常用的操作鼠标划上出现下拉菜单

③ 按钮类应用

我们会用到这种功能往往原因是因为地方小了,按钮多了,这往往说明产品设计一般出问题了。。。 但是,我辈屁民豪不关注产品(没资格插手),所以需要完成以上功能;

其实总的来说,这些功能还是非常实用的。

于是,为了应对以上场景,我工作中先是做了一个,然后又遇到了,然后又遇到了,所以最后就写了这么一个东西。

集中展示


几个功能放到一起了,前端代码如下:

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             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 };

 

 

所以先贴出来和各位看看,后续小生再行优化,希望能把这个功能做好!

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值