/* * 基于jquery級聯(lián)選擇 * 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用于級聯(lián)select。下面就詳細(xì)介紹一下這段代碼:
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'} };
插件的默認(rèn)參數(shù)配置
after:select數(shù)據(jù)載入結(jié)束事件callback
before:數(shù)據(jù)載入前事件callback
usePost:ajax請求方式,默認(rèn)為GET
defaultValue:默認(rèn)選中的值
filter:數(shù)據(jù)過濾函數(shù),類似jq的grep方法,使用方式也是一樣的,第一個參數(shù)為value,第二個參數(shù)為key或index,當(dāng)返回值為false即過濾掉該條數(shù)據(jù),如:filter: function(v, i) { return v.count > 1; }過濾數(shù)據(jù)中count屬性小于1的數(shù)據(jù)
preValue,preText:初始o(jì)ption的值和文字
groupSort:分組optgroup排序方式
order:數(shù)據(jù)的排序方式,排序字段與方向空格隔開
params:隨ajax請求傳遞的參數(shù)
dataMap:數(shù)據(jù)屬性映射,目的提高插件對數(shù)據(jù)的自適應(yīng)性,如真實數(shù)據(jù)是name,id,year,則dataMap: { 'text': 'name', 'value': 'id', 'group': 'year' };如果不使用分組功能,只需保留text和value
missGroup:缺省分組名;
dataToOption:用于將數(shù)據(jù)轉(zhuǎn)換成option;
mapData:將數(shù)據(jù)轉(zhuǎn)成統(tǒng)一格式;
groupData:將數(shù)據(jù)分組;
這段實現(xiàn)邏輯略顯冗長,因為實際項目中的需求多變,這個可根據(jù)實際需求簡化一下代碼。
$.fn.fillselect在dataReadyCallback中觸發(fā)change事件主要目的是使此select的下一級生效;
$.fn.chainselect多了一個target參數(shù)用于指向下一級的jq選擇器。
那么現(xiàn)在來看下怎么使用,需求:廠牌brand->車款kind->車型model,ajax獲取的數(shù)據(jù)格式為{status: 200, data: [{id: 1, name: 'bmw'}, {id: 2, name: 'benz'}]};車型的數(shù)據(jù)需要根據(jù)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 });