基于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 相关文章推荐
javascript document.compatMode兼容性
Feb 23 Javascript
Javascript 类型转换方法
Oct 24 Javascript
script标签的 charset 属性使用说明
Dec 04 Javascript
仅img元素创建后不添加到文档中会执行onload事件的解决方法
Jul 31 Javascript
js中的时间转换—毫秒转换成日期时间的示例代码
Jan 26 Javascript
javascript动态生成树形菜单的方法
Nov 14 Javascript
Angularjs自定义指令实现三级联动 选择地理位置
Feb 13 Javascript
JS触摸事件、手势事件详解
May 04 Javascript
JS实现队列的先进先出功能示例
May 10 Javascript
使用Vue.js 和Chart.js制作绚丽多彩的图表
Jun 15 Javascript
vue通过接口直接下载java生成好的Excel表格案例
Oct 26 Javascript
Vue3.0写自定义指令的简单步骤记录
Jun 27 Vue.js
对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 事件机制(2)
2011/03/23 PHP
PHP生成唯一的促销/优惠/折扣码(附源码)
2012/12/28 PHP
php实现批量修改文件名称的方法
2016/07/23 PHP
php+jQuery ajax实现的实时刷新显示数据功能示例
2019/09/12 PHP
JObj预览一个JS的框架
2008/03/13 Javascript
一个js控制的导航菜单实例代码
2013/12/03 Javascript
jquery实现的网页自动播放声音
2014/04/30 Javascript
jquery实现导航固定顶部的效果仿蘑菇街
2014/10/22 Javascript
js实现同一页面多个不同运动效果的方法
2015/04/10 Javascript
JavaScript设计模式开发中组合模式的使用教程
2016/05/18 Javascript
Javascript+CSS3实现进度条效果
2016/10/28 Javascript
浅谈JS函数节流防抖
2017/10/18 Javascript
vue路由嵌套的SPA实现步骤
2017/11/06 Javascript
node结合swig渲染摸板的方法
2018/04/11 Javascript
VUE中v-on:click事件中获取当前dom元素的代码
2018/08/22 Javascript
gulp构建小程序的方法步骤
2019/05/31 Javascript
记一次react前端项目打包优化的方法
2020/03/30 Javascript
微信小程序学习总结(二)样式、属性、模板操作分析
2020/06/04 Javascript
python基于右递归解决八皇后问题的方法
2015/05/25 Python
Python面向对象类继承和组合实例分析
2018/05/28 Python
Python清空文件并替换内容的实例
2018/10/22 Python
python 实现返回一个列表中出现次数最多的元素方法
2019/06/11 Python
python 机器学习之支持向量机非线性回归SVR模型
2019/06/26 Python
python字符串的拼接方法总结
2019/11/18 Python
python3实现网页版raspberry pi(树莓派)小车控制
2020/02/12 Python
Python使用ElementTree美化XML格式的操作
2020/03/06 Python
初三政治教学反思
2014/01/30 职场文书
超市理货员岗位职责
2014/07/04 职场文书
个人委托书
2014/07/31 职场文书
2015年度服装销售工作总结
2015/03/31 职场文书
观看安全警示教育片心得体会
2016/01/15 职场文书
2019年大学生职业生涯规划书
2019/03/25 职场文书
如何利用Matlab制作一款真正的拼图小游戏
2021/05/11 Python
CSS 使用 resize 实现图片拖拽切换预览功能(强大功能)
2021/08/23 HTML / CSS
Win11如何设置右键单击显示所有选项?Win11右键单击显示所有选项设置教程
2022/04/08 数码科技
pycharm无法安装cv2模块问题
2022/05/20 Python