基于jquery的无限级联下拉框js插件


Posted in Javascript onOctober 29, 2011

灵活性方面考虑了比较多的方面,提供了几个重要的配置方便在各类环境下使用,欢迎各位童鞋使用,源码完全开放。开发这个插件的缘于前段时间维护一个4级级联下拉框被里面200行代码及复杂的结构和bug所郁闷(之所以这么多代码是因为该级联下拉框有时只出现2个或3个),想到这类的需求其实经常都能遇到,jquery里没有这样比较好的插件,索性自己开发个。源代码并不复杂,稍微复杂的地方在第二个插件使用了缓存,造成理解起来十分困难,后面会做些解释。
插件一:适合在不与服务器进行AJAX交互情况使用,需预先将所有下拉框数据全部读出
插件二:适用于每个子级下拉框都post到服务器中取数据绑定。优秀之处在于会将已使用过的数据缓存达到高效率的目的,注意:缓存的键值不仅仅是父下拉框的值,而是从顶级下拉框到当前父下拉框的值组合,这是为了对付出现相同父下拉框对应的子级并不相同的情况。同样的原因,postback中回发给服务器的form表单中也是包括所有的父下拉框的值。

/* 
* 级联下拉框Jqueyr插件,V1.2 
* Copyright 2011, Leo.Liu 
* 本插件包括2个无刷新级联下拉框插件: 
* 插件一:cascadeDropDownData是在不与服务器进行AJAX交互情况使用,需预先将所有下拉框数据全部读出。demo: 
* 方法一:var dataItem = [['全部', '-1', '0'], ['a001', 'a001', '0'], ['a002', 'a002', '0'], ['a003', 'a003', '0'] 
, ['b001', 'b001', 'a001'], ['b002', 'b002', 'a001'], ['b003', 'b003', 'a002'], ['b004', 'b004', 'a003'] 
, ['c001', '001', 'b001'], ['c002', '002', 'b001'], ['c003', '003', 'b002'], ['c004', '004', 'b003']]; 
$.cascadeDropDownBind.bind(dataItem, { sourceID: 'Select1', selectValue: 'a001', parentValue : '0', 
child: { sourceID: 'Select2', selectValue: 'b002', 
child: { sourceID: 'Select3', selectValue: 'c002'} 
} 
}); 
* 此方法有缺陷:a)要求下拉框的值与父子对应中的父值不能相同 b)不能设置全选规则 
* 
* 方法二:var data = [['全部', '0'], ['a001', 'a001'], ['a002', 'a002'], ['a003', 'a003']]; 
var data2 = [['全部', '0', '0'], ['b001', 'b001', 'a001'], ['b002', 'b002', 'a001'], ['b003', 'b003', 'a002'], ['b004', 'b004', 'a003']]; 
var data3 = [['全部', '0', '0'], ['c001', '001', 'b001'], ['c002', '002', 'b001'], ['c003', '003', 'b002'], ['c004', '004', 'b003']]; 
$.cascadeDropDownBind.bind(data, { sourceID: 'Select1', selectValue: 'a001' }); 
$.cascadeDropDownBind.bind(data2, { sourceID: 'Select2', selectValue: 'b002', parentID: 'Select1' }); 
$.cascadeDropDownBind.bind(data3, { sourceID: 'Select3', selectValue: 'c002', parentID: 'Select2' }); 
* 方法三参见cascadeDropDownBind.bind内容 
*/ 
jQuery.extend({ 
//下拉框的数据对象 
cascadeDropDownData: function () { 
//******配置属性******* 
this.removeFirst = true; //是否移除第一个项 
this.appentAllValue = ''; //如果父项目的值等于此值则显示所有项 
this.sourceID = ''; //此下拉框ID 
this.parentID = ''; //父下拉框ID 
this.items = []; //项的数据,二维数组,形式:[['文本', '值', '父ID'],......]; 值和父ID可省略 
this.selectValue = ''; //初始化选中的项 
this.parentValue = null; //用于从一堆数据中筛选出该组所需的项,一般用于绑定第一个下拉框 
//******配置属性******* 
//以下是全局变量,无需在外部设置 
this.child = null; 
var currentDrop = null; 
this.bind = function () { 
currentDrop = $('#' + this.sourceID); 
this.clear(); 
//填充数据项目 
this.fillItem(); 
//设置选中项 
if (this.selectValue) { 
currentDrop.val(this.selectValue); 
} 
//设置父下拉框的change事件 
this.setChange(); 
}; 
//清理填充项目 
this.clear = function () { 
if (this.removeFirst) { 
currentDrop.empty(); 
} else { 
for (var i = currentDrop[0].options.length - 1; i > 0; i--) { 
currentDrop[0].options[i] = null; 
} 
} 
}; 
//填充数据项目 
this.fillItem = function () { 
var _parentValue = this.parentValue; 
if (this.parentID) 
_parentValue = $('#' + this.parentID).val(); 
var all = false; 
if (this.appentAllValue && _parentValue == this.appentAllValue) 
all = true; 
$.each(this.items, function (index, item) { 
var val = item.length > 1 ? item[1] : item[0]; //如果没有指定value则用text代替 
if (all || item.length <= 2 || item[2] == _parentValue) { //如果长度小于3,认为没有父下拉框则填充所有项 
currentDrop.append('<option value="' + val + '">' + item[0] + '</option>'); 
} 
}); 
}; 
//设置父下拉框的change事件 
this.setChange = function () { 
if (this.parentID) { 
$('#' + this.parentID).bind('change', this.change); 
} 
}; 
//父下拉框事件回调函数 
var _this = this; 
this.change = function () { 
_this.clear(); 
_this.fillItem(); 
currentDrop.change(); 
}; 
}, 
cascadeDropDownBind: { 
bind: function (data, setting) { 
var obj = new $.cascadeDropDownData(); 
$.extend(obj, setting); 
obj.items = data; 
obj.bind(); 
if (setting.child) { 
setting.child.parentID = setting.sourceID 
this.bind(data, setting.child); 
} 
} 
} 
}); 
/* 
* 插件二:ajaxDropDownData适用于每个子级下拉框都post到服务器中取数据绑定。 
* 该插件优秀之处在于会将已使用过的数据缓存达到高效率的目的。 
* 注意:缓存的键值不仅仅是父下拉框的值,而是从顶级下拉框到当前父下拉框的值组合,这是为了对付出现相同父下拉框对应的子级并不相同的情况 
* 同样的原因,postback中回发给服务器的form表单中也是包括所有的父下拉框的值 
* 使用方法: 
var firstItems = null; //也可以将第一个下拉框的数据数组放到这进行绑定,或者设置为空,不改变下拉框。 
$.ajaxDropDownBind.bindTopDrop('Select1', firstItems, null, false, 'Select2'); 
$.ajaxDropDownBind.bindCallback('Select2', null, false, 'Select3', 'http://localhost:4167/GetDropDownData.ashx?action=select1'); 
$.ajaxDropDownBind.bindCallback('Select3', null, false, null, 'http://localhost:4167/GetDropDownData.ashx?action=select2'); 
$('#Select1').change(); 
* 最重要的是级联的ID设置不能缺少。高级用法参见$.ajaxDropDownBind.bindCallback方法的内容。 
*/ 
jQuery.extend({ 
//此对象是个链表结构 
ajaxDropDownData: function () { 
//******配置属性******* 
this.sourceID = ''; //此下拉框ID 
this.items = []; //此项的数据,二维数组,形式:[['文本', '值', '父ID'],......]; 值和父ID可省略 
this.callback = null; //回调函数,用于获取下一级的方法,此函数接收2个参数:value, dropdownlist分别代表父级下拉框选中的值及本身的实例 
this.childID = ''; //关联的子控件ID 
this.removeFirst = true; //是否移除该项第一个选项 
this.selectValue = ''; //此项初始化选中的值 
//******配置属性******* 
//********************下面是系统变量及方法**************************************************** 
this.childItem = []; //子对象列表,用于缓存 
this.parentObj = null; //父对象 
this.canChange = true; 
this.clearItem = true; 
this.bind = function () { 
$.ajaxDropDownBind.bindData(this); 
}; 
this.bindData = function (setting) { 
$.extend(this, setting); 
$.ajaxDropDownBind.bindData(this); 
}; 
/*回溯到根节点,从根节点开始按照级联取当前正确的下拉框的对象 
由于下拉框的事件只有一个,而对应的对象有多个,所以这里需要先回溯到根,在从根开始找起 
*/ 
this.getRightOnChangeObj = function () { 
if (this.parentObj) 
return this.parentObj.getRightOnChangeObj().childItem[$('#' + this.parentObj.sourceID).val()]; //递归 
else 
return this; 
} 
}, 
ajaxDropDownBind: { 
currentDrop: null, 
_thisData: null, 
callbackPool: [], 
//清理填充项目 
clear: function () { 
if (_thisData.removeFirst) { 
currentDrop.empty(); 
} else { 
for (var i = currentDrop[0].options.length - 1; i > 0; i--) { 
currentDrop[0].options[i] = null; 
} 
} 
}, 
//填充数据项目 
fillItem: function () { 
for (var i = 0; i < _thisData.items.length; i++) { 
var val = _thisData.items[i].length > 1 ? _thisData.items[i][1] : _thisData.items[i][0]; //如果没有指定value则用text代替 
currentDrop.append('<option value="' + val + '">' + _thisData.items[i][0] + '</option>'); 
} 
//设置选中项 
if (_thisData.selectValue) { 
currentDrop.val(_thisData.selectValue); 
_thisData.selectValue = ''; 
} 
}, 
//参数data是指当前变化的下拉框所在的对象 
bindData: function (data) { 
_thisData = data; 
currentDrop = $('#' + _thisData.sourceID); //本身的节点而不是子级 
if (_thisData.clearItem) 
this.clear(); 
if (_thisData.items) 
this.fillItem(); 
if (_thisData.childID) { 
if (!currentDrop.data('binded')) { //判断是否绑定过事件,绑定过的不在绑定 
currentDrop.data('binded', true); 
var _firstChangeObj = _thisData; //由于下拉框的事件只有一个,而对应的对象有多个,所以这里的对象是绑定时的对象,而非正确的对象 
currentDrop.bind('change', function () { 
var rightChildItem = _firstChangeObj.getRightOnChangeObj().childItem; 
var thisValue = $('#' + _firstChangeObj.sourceID).val(); //获取当前变化的下拉框的值,注意不能用currentDrop代替,因为currentDrop也是旧的值 
if (rightChildItem[thisValue]) { 
console.log('cache'); 
rightChildItem[thisValue].bind(); //使用缓存的数据来绑定 
} 
else { 
console.log('getdata'); 
//一个新的实例,并建立链表关系 
var dropData = new $.ajaxDropDownData(); 
dropData.sourceID = _firstChangeObj.childID; 
dropData.parentObj = _firstChangeObj; 
rightChildItem[thisValue] = dropData; //设置缓存 
if (_firstChangeObj.callback) //如果有回调函数则直接调用,否则调用系统自动生成的函数 
_firstChangeObj.callback(thisValue, dropData); //回调函数 
else { 
var callback = $.ajaxDropDownBind.callbackPool[dropData.sourceID]; 
if (callback) 
callback(thisValue, dropData); 
} 
} 
}); 
} 
if (_thisData.canChange) 
currentDrop.change(); 
} 
}, 
//绑定第一级下拉框 
bindTopDrop: function (sourceID, items, selectValue, removeFirst, childID) { 
var mydrop = new $.ajaxDropDownData(); 
mydrop.sourceID = sourceID; 
mydrop.items = items; 
if (!items || items.length == 0) 
mydrop.clearItem = false; 
mydrop.removeFirst = removeFirst; 
mydrop.selectValue = selectValue; 
mydrop.childID = childID; 
mydrop.canChange = false; 
mydrop.bind(); 
}, 
//绑定子级下拉框 
bindCallback: function (sourceID, selectValue, removeFirst, childID, parentPostUrl) { 
var callback = function (value, dropdownlist) { 
var postData = {}; 
var parentObj = dropdownlist.parentObj; 
while (parentObj) { 
postData[parentObj.sourceID] = $('#' + parentObj.sourceID).val(); 
parentObj = parentObj.parentObj; 
} 
$.getJSON(parentPostUrl + '&jsoncallback=?', postData, function (data) { 
dropdownlist.items = data; 
dropdownlist.removeFirst = removeFirst; 
dropdownlist.selectValue = selectValue; 
dropdownlist.childID = childID; 
dropdownlist.bind(); 
}); 
}; 
this.callbackPool[sourceID] = callback; 
} 
} 
});

使用方法代码里有注释,不赘述,欢迎拍砖。
不知道怎么添加附件,把测试代码也一并贴上来,因为插件二需要服务器端的配合这里就不贴插件二的测试代码。
插件一测试代码
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head runat="server"> 
<title>无限极级联下拉框的封装</title> 
<script type="text/javascript" src="http://test.fe.ecp.mic.cn/content/scripts/base/jquery.js"></script> 
<style> 
select 
{ 
margin-left: 10px; 
} 
body 
{ 
font-size: 14px; 
background-color: #CCCCCC; 
} 
td 
{ 
border: 1px solid #FF6600; 
padding: 5px; 
text-align: left; 
} 
input 
{ 
width: 80px; 
} 
input[type=checkbox] 
{ 
width: 20px; 
} 
</style> 
</head> 
<body> 
<form id="form1" runat="server"> 
<div> 
<h1> 
无限极级联下拉框的封装</h1> 
<div style="margin: 50px 0 50px 10px;"> 
绑定方法:<select id="selCase"><option value="1">第一种方法</option> 
<option value="2">第二种方法</option> 
</select> 
<input type="button" onclick="test()" value="绑定" /> 
<div style="margin: 10px;"> 
<table cellpadding="0" cellspacing="0"> 
<tr> 
<td> 
第一个下拉框: 
</td> 
<td> 
<input type="text" id="tb1sel" value="a002" />设置当前项的选中值 
</td> 
<td> 
</td> 
<td> 
</td> 
</tr> 
<tr> 
<td> 
第二个下拉框: 
</td> 
<td> 
<input type="text" id="tb2sel" value="" />设置当前项的选中值 
</td> 
<td> 
<input type="checkbox" id="cb2Remove" value="" />是否移除第一项 
</td> 
<td> 
<input type="text" id="tb2AllValue" value="0" />当前一项值等于此值时,该项全选 
</td> 
</tr> 
<tr> 
<td> 
第三个下拉框: 
</td> 
<td> 
<input type="text" id="tb3sel" value="" />设置当前项的选中值 
</td> 
<td> 
<input type="checkbox" id="cb3Remove" />是否移除第一项 
</td> 
<td> 
<input type="text" id="tb3AllValue" value="" />当前一项值等于此值时,该项全选 
</td> 
</tr> 
</table> 
</div> 
</div> 
<h4> 
下拉框结果:</h4> 
<div id="selContainer" style="margin: 0px 0 50px 100px;"> 
</div> 
<script type="text/javascript" src="/Scripts/Jquery.cascadeDropDown-1.2.js"></script> 
<script type="text/javascript"> 
$(function () { 
toggleSetting(); 
$('#selCase').bind('change', toggleSetting); 
}); 
function toggleSetting() { 
if ($('#selCase').val() == '1') 
$('table tr').each(function (i, item) { 
$($(item).find('td')[3]).hide(); 
}); 
else 
$('table tr').each(function (i, item) { 
$($(item).find('td')[3]).show(); 
}); 
} 
function test() { 
if ($('#selCase').val() == '1') 
testcase1(); 
else 
testcase2(); 
} 
function testcase1() { 
$('#Select1').remove(); 
$('#Select2').remove(); 
$('#Select3').remove(); 
$('#selContainer').html('<select id="Select1"><option value="-1">全部</option></select><select id="Select2"><option value="-1">全部</option></select><select id="Select3"><option value="-1">全部</option></select>'); 
var dataItem = [['a001', 'a001', '0'], ['a002', 'a002', '0'], ['a003', 'a003', '0'] 
, ['b001', 'b001', 'a001'], ['b002', 'b002', 'a001'], ['b003', 'b003', 'a002'], ['b004', 'b004', 'a002'], ['b005', 'b005', 'a003'] 
, ['c001', '001', 'b001'], ['c002', '002', 'b001'], ['c003', '003', 'b002'], ['c004', '004', 'b002'], ['c005', '005', 'b003'], ['c006', '006', 'b004'], ['c007', '007', 'b004'], ['c008', '008', 'b005'] 
]; 
$.cascadeDropDownBind.bind(dataItem, 
{ sourceID: 'Select1', selectValue: $('#tb1sel').val(), parentValue: '0', removeFirst: false, 
child: { sourceID: 'Select2', selectValue: $('#tb2sel').val(), removeFirst: $('#cb2Remove').attr('checked'), 
child: { sourceID: 'Select3', selectValue: $('#tb3sel').val(), removeFirst: $('#cb3Remove').attr('checked') } 
} 
} 
); 
} 
function testcase2() { 
$('#Select1').remove(); 
$('#Select2').remove(); 
$('#Select3').remove(); 
//$('#selContainer').html('<select id="Select1"></select><select id="Select2"></select><select id="Select3"></select>'); 
$('#selContainer').html('<select id="Select1"><option value="0">全部</option></select><select id="Select2"><option value="0">全部</option></select><select id="Select3"><option value="0">全部</option></select>'); 
var data = [['a001', 'a001'], ['a002', 'a002'], ['a003', 'a003']]; 
var data2 = [['b001', 'b001', 'a001'], ['b002', 'b002', 'a001'], ['b003', 'b003', 'a002'], ['b004', 'b004', 'a002'], ['b005', 'b005', 'a003']]; 
var data3 = [['c001', '001', 'b001'], ['c002', '002', 'b001'], ['c003', '003', 'b002'], ['c004', '004', 'b002'], ['c005', '005', 'b003'], ['c006', '006', 'b004'], ['c007', '007', 'b004'], ['c008', '008', 'b005']]; 
$.cascadeDropDownBind.bind(data, { sourceID: 'Select1', selectValue: $('#tb1sel').val(), removeFirst: false }); 
$.cascadeDropDownBind.bind(data2, { sourceID: 'Select2', selectValue: $('#tb2sel').val(), parentID: 'Select1', removeFirst: $('#cb2Remove').attr('checked'), appentAllValue: $('#tb2AllValue').val() }); 
$.cascadeDropDownBind.bind(data3, { sourceID: 'Select3', selectValue: $('#tb2sel').val(), parentID: 'Select2', removeFirst: $('#cb3Remove').attr('checked'), appentAllValue: $('#tb3AllValue').val() }); 
} 
</script> 
</div> 
</form> 
</body> 
</html>
Javascript 相关文章推荐
Jquery为单选框checkbox绑定单击click事件
Dec 18 Javascript
JS 屏蔽按键效果与改变按键效果的示例代码
Dec 24 Javascript
jquery教程限制文本框只能输入数字和小数点示例分享
Jan 13 Javascript
jQuery模拟物体自由落体运动(附演示与demo源码下载)
Jan 21 Javascript
jQuery和hwSlider实现内容响应式可触控滑动切换效果附源码下载(二)
Jun 22 Javascript
jQuery实现表格行和列的动态添加与删除方法【测试可用】
Aug 01 Javascript
jquery Banner轮播选项卡
Dec 26 Javascript
超简单的Vue.js环境搭建教程
Mar 17 Javascript
基于Vue实现后台系统权限控制的示例代码
Aug 29 Javascript
webpack本地开发环境无法用IP访问的解决方法
Mar 20 Javascript
angularJs利用$scope处理升降序的方法
Oct 08 Javascript
基于Vue的商品主图放大镜方案详解
Sep 19 Javascript
对setInterval在火狐和chrome切换标签产生奇怪的效果之探索,与解决方案!
Oct 29 #Javascript
仿猪八戒网左下角的文字滚动效果
Oct 28 #Javascript
js实现双向链表互联网机顶盒实战应用实现
Oct 28 #Javascript
js常用代码段收集
Oct 28 #Javascript
jWiard 基于JQuery的强大的向导控件介绍
Oct 28 #Javascript
理解JSON:3分钟课程
Oct 28 #Javascript
Kibo 用于处理键盘事件的Javascript工具库
Oct 28 #Javascript
You might like
codeigniter集成ucenter1.6双向通信的解决办法
2014/06/12 PHP
ThinkPHP提示错误Fatal error: Allowed memory size的解决方法
2015/02/12 PHP
php实现以只读方式打开文件的方法
2015/03/16 PHP
php限制ip地址范围的方法
2015/03/31 PHP
php基于curl重写file_get_contents函数实例
2016/11/08 PHP
[企业公众号]升级到[企业微信]之后发送消息失败的解决方法
2017/06/30 PHP
PHP微商城开源代码实例
2019/03/27 PHP
限制复选框最多选择项的实现代码
2016/05/30 Javascript
AngularJS封装指令方法详解
2016/12/12 Javascript
JS前端加密算法示例
2016/12/22 Javascript
JS实现颜色动态淡化效果
2017/03/06 Javascript
javascript实现table单元格点击展开隐藏效果(实例代码)
2017/04/10 Javascript
jQuery Json数据格式排版高亮插件json-viewer.js使用方法详解
2017/06/12 jQuery
IntersectionObserver实现图片懒加载的示例
2017/09/29 Javascript
ES6学习笔记之map、set与数组、对象的对比
2018/03/01 Javascript
JS自定义滚动条效果
2020/03/13 Javascript
vuex的数据渲染与修改浅析
2020/11/26 Vue.js
JavaScript 生成唯一ID的几种方式
2021/02/19 Javascript
[54:10]Spirit vs NB Supermajor小组赛 A组败者组决赛 BO3 第一场 6.2
2018/06/03 DOTA
python实现中文分词FMM算法实例
2015/07/10 Python
详解常用查找数据结构及算法(Python实现)
2016/12/09 Python
python处理xml文件的方法小结
2017/05/02 Python
Python内置函数 next的具体使用方法
2017/11/24 Python
基于Python函数的作用域规则和闭包(详解)
2017/11/29 Python
python去重,一个由dict组成的list的去重示例
2019/01/21 Python
python解析多层json操作示例
2019/12/30 Python
python3 使用ssh隧道连接mysql的操作
2020/12/05 Python
悬挂训练绳:TRX
2017/12/14 全球购物
Auguste The Label官网:澳大利亚一家精品女装时尚品牌
2020/06/14 全球购物
经典优秀个人求职自荐信格式
2013/09/25 职场文书
刘胡兰的英雄事迹材料
2014/02/11 职场文书
幼儿园六一儿童节文艺汇演主持词
2014/03/21 职场文书
祖国在我心中演讲稿300字
2014/05/04 职场文书
Mysql 如何批量插入数据
2021/04/06 MySQL
为什么代码规范要求SQL语句不要过多的join
2021/06/23 MySQL
OpenCV实现反阈值二值化
2021/11/17 Java/Android