基于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中使用Ajax获取JSON格式数据示例代码
Nov 26 Javascript
js中各种类型的变量在if条件中是true还是false
Jul 16 Javascript
浅谈被jQuery抛弃的函数及替代函数
May 03 Javascript
学习JavaScript设计模式之策略模式
Jan 12 Javascript
jQuery实现打开页面渐现效果示例
Jul 27 Javascript
Vue.js快速入门实例教程
Oct 15 Javascript
如何使用headjs来管理和异步加载js
Nov 29 Javascript
js数字舍入误差以及解决方法(必看篇)
Feb 28 Javascript
bootstrap table实现x-editable的行单元格编辑及解决数据Empty和支持多样式问题
Aug 10 Javascript
js中的 || 与 &amp;&amp; 运算符详解
May 24 Javascript
使用Vue.js中的过滤器实现幂方求值的方法
Aug 27 Javascript
jQuery实现每日秒杀商品倒计时功能
Sep 06 jQuery
对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创建桌面快捷方式的实例代码
2014/02/17 PHP
ThinkPHP框架搭建及常见问题(XAMPP安装失败、Apache/MySQL启动失败)
2016/04/15 PHP
PHP实现的随机IP函数【国内IP段】
2016/07/20 PHP
详细解读php的命名空间(二)
2018/02/21 PHP
实现PHP中session存储及删除变量
2018/10/15 PHP
JS正则表达式大全(整理详细且实用)
2013/11/14 Javascript
jquery实现手风琴效果实例代码
2013/11/15 Javascript
jquery动态添加元素事件失效问题解决方法
2014/05/23 Javascript
js滑动提示效果代码分享
2016/03/10 Javascript
JQuery实现动态操作表格
2017/01/11 Javascript
Angular弹出模态框的两种方式
2017/10/19 Javascript
详解tween.js 中文使用指南
2018/01/05 Javascript
值得收藏的八个常用的js正则表达式
2018/10/19 Javascript
微信小程序实现通过双向滑动缩放图片大小的方法
2018/12/30 Javascript
Node.js系列之连接DB的方法(3)
2019/08/30 Javascript
Layui Table js 模拟选中checkbox的例子
2019/09/03 Javascript
js实现中文实时时钟
2020/01/15 Javascript
在Vue中使用HOC模式的实现
2020/08/23 Javascript
phpsir 开发 一个检测百度关键字网站排名的python 程序
2009/09/17 Python
详解Python3操作Mongodb简明易懂教程
2017/05/25 Python
Python提取特定时间段内数据的方法实例
2019/04/01 Python
2019 Python最新面试题及答案16道题
2019/04/11 Python
Django中的用户身份验证示例详解
2019/08/07 Python
Python如何调用JS文件中的函数
2019/08/16 Python
Python爬虫 urllib2的使用方法详解
2019/09/23 Python
python实现上传文件到linux指定目录的方法
2020/01/03 Python
new_zeros() pytorch版本的转换方式
2020/02/18 Python
Python可以用来做什么
2020/11/23 Python
python 多线程爬取壁纸网站的示例
2021/02/20 Python
Java的类可以定义为Protected或者Private得吗
2015/09/25 面试题
前台文员的岗位职责
2013/11/14 职场文书
办公室前台的岗位职责
2013/12/20 职场文书
2015年电厂工作总结范文
2015/05/13 职场文书
交通事故代理词范文
2015/05/23 职场文书
中学团支部工作总结
2015/08/13 职场文书
2020年基层司法所建设情况调研报告
2019/11/30 职场文书