zoukankan      html  css  js  c++  java
  • 基于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=" 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://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 };

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

  • 相关阅读:
    如何打印调试字符串?
    如何测试代码执行耗时?
    access 如何导出 cvs 文件?
    opencv如何打印长图?
    window 注册表上下文菜单如何配置?
    python 零散记录(四) 强调字典中的键值唯一性 字典的一些常用方法
    python 零散记录(三) 格式化字符串 字符串相关方法
    python 零散记录(二) 序列的相关操作 相加 相乘 改变 复制
    python 零散记录(一) input与raw_input 数学相关函数 转换字符串的方法
    devi into python 笔记(七)locals与globals 字典格式化字符串 字符集
  • 原文地址:https://www.cnblogs.com/yexiaochai/p/2988939.html
Copyright © 2011-2022 走看看