基于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 相关文章推荐
Div Select挡住的解决办法
Aug 07 Javascript
select组合框option的捕捉实例代码
Sep 30 Javascript
基于javascript实现图片懒加载
Jan 05 Javascript
7个jQuery最佳实践
Jan 12 Javascript
jquery使用on绑定a标签无效 只能用live解决
Jun 02 Javascript
通过JS和PHP两种方法判断用户请求时使用的浏览器类型
Sep 01 Javascript
JS验证input输入框(字母,数字,符号,中文)
Mar 23 Javascript
vue学习笔记之指令v-text &amp;&amp; v-html &amp;&amp; v-bind详解
May 12 Javascript
JavaScript的Object.defineProperty详解
Jul 09 Javascript
vue 组件的封装之基于axios的ajax请求方法
Aug 11 Javascript
javascript将非数值转换为数值
Sep 13 Javascript
浅析我对JS延迟异步脚本的思考
Oct 12 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
PHP ? EasyUI DataGrid 资料取的方式介绍
2012/11/07 PHP
YII框架常用技巧总结
2019/04/27 PHP
PHP面向对象程序设计__tostring()和__invoke()用法分析
2019/06/12 PHP
js focus不起作用的解决方法(主要是因为dom元素是否加载完成)
2010/11/05 Javascript
HTML5附件拖拽上传drop &amp; google.gears实现代码
2011/04/28 Javascript
javascript 进阶篇3 Ajax 、JSON、 Prototype介绍
2012/03/14 Javascript
ajax异步刷新实现更新数据库
2012/12/03 Javascript
原生javaScript做得动态表格(注释写的很清楚)
2013/12/29 Javascript
JavaScript中的getDay()方法使用详解
2015/06/09 Javascript
JS实现支持多选的遍历下拉列表代码
2015/08/20 Javascript
Webpack中css-loader和less-loader的使用教程
2017/04/27 Javascript
Angular 组件之间的交互的示例代码
2018/03/24 Javascript
浅谈node中的cluster集群
2018/06/02 Javascript
Vue2.X 通过AJAX动态更新数据
2018/07/17 Javascript
angularjs性能优化的方法
2018/09/05 Javascript
Vue.js样式动态绑定实现小结
2019/01/24 Javascript
Vue如何实现监听组件原生事件
2020/07/03 Javascript
Vue清除定时器setInterval优化方案分享
2020/07/21 Javascript
js实现详情页放大镜效果
2020/10/28 Javascript
[05:05]第三天的dota2
2013/07/29 DOTA
[09:13]2014DOTA2国际邀请赛 中国区预选赛coser表演
2014/05/23 DOTA
[01:13]这,就是刀塔
2014/07/16 DOTA
python统计文本文件内单词数量的方法
2015/05/30 Python
Python检测生僻字的实现方法
2016/10/23 Python
Python3 使用selenium插件爬取苏宁商家联系电话
2019/12/23 Python
波比布朗英国官网:Bobbi Brown英国
2017/11/13 全球购物
以思科路由器为例你写下单臂路由的配置命令
2013/08/03 面试题
小学教师培训感言
2014/02/11 职场文书
留学生求职信
2014/06/03 职场文书
分公司任命书
2014/06/06 职场文书
学校志愿者活动总结
2014/06/27 职场文书
公司离职证明标准格式
2014/11/18 职场文书
承诺书范本
2015/01/21 职场文书
公司董事任命书
2015/09/21 职场文书
MySQL中的布尔值,怎么存储false或true
2021/06/04 MySQL
MySQL时区造成时差问题
2022/04/13 MySQL