现在的位置: 首页 > 综合 > 正文

jquery级联下拉插件

2012年10月26日 ⁄ 综合 ⁄ 共 5274字 ⁄ 字号 评论关闭

太久没有写博客了,废话就不多说,直接上干货

/* 
 * 基於jquery級聯選擇
 * target: 下一級的jquery選擇器
 * urlOrData: ajax請求的url或用於篩選的Data
 * options: 配置
 */
(function($) {
    var defaultOptions = {
        after: null,
        before: null,
        usePost: false,
        defaultValue: null,
        filter: null,
        preValue: '-1',
        preText: '請選擇',
        groupSort: 'desc', 
        order: 'name asc',
        params: {},
        dataMap: {'text': 'text', 'value': 'value', 'group': 'group'}
    };
    var missGroup = '__MISS__';
    var dataToOption = function(data, op) {
        var d = [], vs = [], hasFilter = op.filter && $.isFunction(op.filter);
        $.each(data, function(i, v) {
            if(!hasFilter || (hasFilter && op.filter(v, i))) {
                if($.inArray(v[op.dataMap.value], vs) == -1) {
                    d.push($.extend({}, v));
                    vs.push(v[op.dataMap.value]);
                }
            }
        });
        if(op.order) {
            var sp = op.order.split(' '), col = sp[0], sort = sp[1].toLowerCase() == 'asc' ? 1 : -1;
            d.sort(function(a, b) {
                if(a[col] > b[col]) {
                    return sort;
                } else if(a[col] < b[col]) {
                    return -sort;
                } else {
                    return 0;
                }
            });
        }
        var ops = '', gps = groupData(mapData(d, op.dataMap));
        var createOption = function(items) {
            var _ops = '';
            if(!$.isArray(items)) {
                items = [items];
            }
            $.each(items, function(i, v) {
                _ops += '<option value="' + v['value'] + '">' + v['text'] + '</option>';
            });
            return _ops;
        };
        var createGroup = function(group, options) {
            return '<optgroup label="' + group + '"></optgroup>' + options;
        };
        if(op.preValue || op.preText) {
            ops += createOption({'value': op.preValue, 'text': op.preText});
        }
        if(gps[missGroup] != undefined) {
            ops += createOption(gps[missGroup]);
            delete gps[missGroup];
        }
        if(op.groupSort == 'desc') {
            gps['keys'].sort().reverse();
        } else if(op.groupSort == 'asc') {
            gps['keys'].sort()
        }
        $.each(gps['keys'], function(i, v) {
            ops += createGroup(v, createOption(gps[v]));
        });
        return ops;
    };
    var mapData = function(data, map) { 
        $.each(data, function(i, v) {
            $.each(map, function(j, k) {
                if(v[j] == undefined) {
                    data[i][j] = v[k] == undefined ? '' : v[k];
                    delete data[i][k];
                }
            });
        });
        return data;
    };
    var groupData = function(data) {
        var gps = {};
        gps['keys'] = [];
        var pushData = function(group, item) {
            if(gps[group] == undefined) {
                gps[group] = [];
            }
            gps[group].push(item);
        };
        var pushKey = function(key) {
            if($.inArray(key, gps['keys']) == -1) {
                gps['keys'].push(key);
            }
        };
        $.each(data, function(i, v) {
            if(v['group']) {
                pushKey(v['group']);
                pushData(v['group'], v);
            } else {
                pushData(missGroup, v);
            }
        });
        return gps;
    };
    $.fn.fillselect = function(urlOrData, options) {
        return this.each(function() {
            var $t = $(this), op, dataReadyCallback, ajaxXHR = null;
            op = $.extend(true, {}, defaultOptions, options);
            $t.data('fillselectOptions', op);
            if(op.before && $.isFunction(op.before)) {
                op.before.apply($t);
            }
            dataReadyCallback = function(data) {
                $t.html(dataToOption(data, op));
                if(op.defaultValue) {
                    $t.val(op.defaultValue);
                }
                if(op.after && $.isFunction(op.after)) {
                    op.after.apply($t);
                }
                $t.trigger('change');
            };
            if(typeof urlOrData == 'string') {
                if(ajaxXHR) {
                    ajaxXHR.abort();
                }
                ajaxXHR = $.ajax({
                    'url': urlOrData,
                    'type': op.usePost ? 'post' : 'get',
                    'data': op.params,
                    'dataType': 'json',
                    'success': function(data) {
                        ajaxXHR = null;
                        if(data.status == '200') {
                            dataReadyCallback(data.data || []);
                        }
                    },
                    'error': function() {
                        ajaxXHR = null;
                    }
                });
            } else {
                dataReadyCallback(urlOrData);
            }
        });
    };
    $.fn.chainselect = function(target, urlOrData, options) {
        return this.each(function() {
            $(this).bind('change', function() {
                var $t = $(this), op;
                op = $.extend(true, {}, defaultOptions , options);
                op.params[$t.attr('name') || $t.attr('id')] = $t.val();
                op.params['id'] = $t.val();
                if($t.val() != $t.data('fillselectOptions').preValue) {
                    $(target).fillselect(urlOrData, op);
                } else {
                    op.after = null;
                    $(target).fillselect([], op);
                }
            });
        });
    }
})(jQuery);

此段代码实际上包含了两个jq插件,第一个是fillselect用于填充select,支持data或url(ajax)的方式;第二个是chainselect用于级联select。下面就详细介绍一下这段代码:

var defaultOptions = {
        after: null,
        before: null,
        usePost: false,
        defaultValue: null,
        filter: null,
        preValue: '-1',
        preText: '請選擇',
        groupSort: 'desc', 
        order: 'name asc',
        params: {},
        dataMap: {'text': 'text', 'value': 'value', 'group': 'group'}
    };

 

插件的默认参数配置

  • after:select数据载入结束事件callback
  • before:数据载入前事件callback
  • usePost:ajax请求方式,默认为GET
  • defaultValue:默认选中的值
  • filter:数据过滤函数,类似jq的grep方法,使用方式也是一样的,第一个参数为value,第二个参数为key或index,当返回值为false即过滤掉该条数据,如:filter: function(v, i) { return v.count > 1; }过滤数据中count属性小于1的数据
  • preValue,preText:初始option的值和文字
  • groupSort:分组optgroup排序方式
  • order:数据的排序方式,排序字段与方向空格隔开
  • params:随ajax请求传递的参数
  • dataMap:数据属性映射,目的提高插件对数据的自适应性,如真实数据是name,id,year,则dataMap: { 'text': 'name', 'value': 'id', 'group': 'year' };如果不使用分组功能,只需保留text和value

missGroup:缺省分组名;

dataToOption:用于将数据转换成option;

mapData:将数据转成统一格式;

groupData:将数据分组;

这段实现逻辑略显冗长,因为实际项目中的需求多变,这个可根据实际需求简化一下代码。

 

$.fn.fillselect在dataReadyCallback中触发change事件主要目的是使此select的下一级生效;

$.fn.chainselect多了一个target参数用于指向下一级的jq选择器。

那么现在来看下怎么使用,需求:厂牌brand->车款kind->车型model,ajax获取的数据格式为{status: 200, data: [{id: 1, name: 'bmw'}, {id: 2, name: 'benz'}]};车型的数据需要根据year_name分组

<select name="brand" id="brand"></select>
<select name="kind" id="kind" disabled></select>
<select name="model" id="model" disabled></select>
    $('#brand').chainselect('#kind', '/request_url', {
            'dataMap': {'value': 'id', 'text': 'name'}, 
            'preValue': -1,
            'preText': '選擇/車款',
            'params': {'type': '_cache_kind'},
            'before': function() {
                this.attr('disabled', true);
            },
            'after': function() {
                this.removeAttr('disabled');
            },
            'defaultValue': lastKindId || -1
        });
        $('#kind').chainselect('#model', '/request_url', {
            'dataMap': {'value': 'myid', 'text': 'name', 'group': 'year_name'}, 
            'preValue': -1,
            'preText': '選擇/車型',
            'params': {'type': '_cache_model'},
            'filter': function(v, i) {
                return $.inArray(v.myid, myids) === -1;
            },
            'before': function() {
                this.attr('disabled', true);
            },
            'after': function() {
                this.removeAttr('disabled');
            }
        });
        $('#brand').fillselect('/request_url', {
            'dataMap': {'value': 'id', 'text': 'name'}, 
            'preValue': -1,
            'preText': '選擇/廠牌',
            'params': {'type': '_cache_brand'},
            'defaultValue': lastBrandId || -1
        });

 

抱歉!评论已关闭.