基于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 相关文章推荐
js 幻灯片的实现
Dec 06 Javascript
JavaScript在多浏览器下for循环的使用方法
Nov 07 Javascript
js动态生成Html元素实现Post操作(createElement)
Sep 14 Javascript
vueJS简单的点击显示与隐藏的效果【实现代码】
May 03 Javascript
JavaScript实现简易的天数计算器实例【附demo源码下载】
Jan 18 Javascript
详解使用grunt完成requirejs的合并压缩和js文件的版本控制
Mar 02 Javascript
vue指令以及dom操作详解
Mar 04 Javascript
ES6深入理解之“let”能替代”var“吗?
Jun 28 Javascript
使用jQuery 操作table 完成单元格合并的实例
Dec 27 jQuery
React如何避免重渲染
Apr 10 Javascript
vue.js input框之间赋值方法
Aug 24 Javascript
javascript实现贪吃蛇游戏(娱乐版)
Aug 17 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
在WordPress的文章编辑器中设置默认内容的方法
2015/12/29 PHP
jsTree树控件(基于jQuery, 超强悍)[推荐]
2009/09/01 Javascript
在一个浏览器里呈现所有浏览器测试结果的前端测试工具的思路
2010/03/02 Javascript
jQuery调用RESTful WCF示例代码(GET方法/POST方法)
2014/01/26 Javascript
Javascript获取表单名称(name)的方法
2015/04/02 Javascript
nodejs调用cmd命令实现复制目录
2015/05/04 NodeJs
JavaScript中Form表单技术汇总(推荐)
2016/06/26 Javascript
jQuery查找节点并获取节点属性的方法
2016/09/09 Javascript
将鼠标焦点定位到文本框最后(代码分享)
2017/01/11 Javascript
详解vue-validator(vue验证器)
2017/01/16 Javascript
JavaScript实现时钟滴答声效果
2017/01/29 Javascript
修改vue+webpack run build的路径方法
2018/09/01 Javascript
小程序组件之仿微信通讯录的实现代码
2018/09/12 Javascript
微信小程序淘宝首页双排图片布局排版代码(推荐)
2020/10/29 Javascript
Python3基础之基本运算符概述
2014/08/13 Python
Python计算回文数的方法
2015/03/11 Python
Python实现Kmeans聚类算法
2020/06/10 Python
Django处理多用户类型的方法介绍
2019/05/18 Python
Django组件cookie与session的具体使用
2019/06/05 Python
PyQt5 控件字体样式等设置的实现
2020/05/13 Python
利用纯CSS3实现动态的自行车特效源码
2017/01/20 HTML / CSS
html5调用app分享功能示例(WebViewJavascriptBridge)
2018/03/21 HTML / CSS
法国春天百货官网:Printemps.com
2020/06/29 全球购物
会计专业自荐信范文
2013/12/02 职场文书
计算机学生求职信范文
2014/01/30 职场文书
男女朋友协议书
2014/04/23 职场文书
贷款担保书范文
2014/05/13 职场文书
奥林匹克的口号
2014/06/13 职场文书
关于运动会的广播稿
2014/09/22 职场文书
机票销售员态度不好检讨书
2014/09/27 职场文书
计生办班子群众路线教育实践活动个人对照检查材料思想汇报
2014/10/04 职场文书
世界地球日活动总结
2015/02/09 职场文书
幼儿园见习总结
2015/06/23 职场文书
图解上海144收音机
2021/04/22 无线电
javascript对象3个属性特征
2021/11/17 Javascript
Python中np.random.randint()参数详解及用法实例
2022/09/23 Python