码迷,mamicode.com
首页 > 其他好文 > 详细

BootStrap-DualListBox怎样改造成为双树

时间:2015-12-18 16:26:18      阅读:1998      评论:0      收藏:0      [点我收藏+]

标签:

BootStrap-DualListBox能够实现将所选择的列表项显示到右边,未选的列表项显示到左边。

但是左右两边的下拉框中都是单级列表。如果要实现将两边都是树(缩进树),选择某个节点时,其子节点也进到右边,不选某个节点时,其子节点也都回到左边呢?

实现思路是:

1、在DualListBox每次选择时,都会触发change事件,我们在change中,去处理子节点的选择和未选择。所有处理都通过change事件触发。

2、在处理完后,调用DualListBox的refresh方法。

在具体处理中,需要遍历树的节点数据,来获取树节点,子节点,父节点,并进行递归处理。

为了方便调用,将改进后扩展的代码放到单独的文件中,并扩展了jquery方法,增加BootDualTree方法,实现双树的初始化,加载数据,获取选中值,反向绑定等方法。

调用代码示例如下:

  1   <head>
  2     <title>Bootstrap Dual Listbox</title>
  3     <link href="bootstrap.min.css" rel="stylesheet">
  4       <!--<link href="http://libs.baidu.com/bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">-->
  5     <link rel="stylesheet" type="text/css" href="../src/prettify.css">
  6     <link rel="stylesheet" type="text/css" href="../src/bootstrap-duallistbox.css">
  7     <script src="jquery.min.js"></script>
  8       <!--<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>-->
  9     <script src="bootstrap.min.js"></script>
 10 
 11       <!--<script src="http://libs.baidu.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>-->
 12     <script src="../src/jquery.bootstrap-duallistbox.js"></script>
 13       <script src="bootstrap-dualtree.js"></script>
 14   </head>
 15   <body class="container">
 16 
 17 
 18 
 19       <h2>lh Test</h2>
 20       <p>
 21           Make the dual listbox be dual tree.
 22       </p>
 23       <div>
 24           <form id="lhdemoform" action="#" method="post">
 25               <select multiple="multiple" size="10" name="duallistbox_lhdemo">
 26                   <!--<option value="option1">Option 1</option>-->
 27               </select>
 28               <br> 
 29               <input type="button" value="初始数据" id="btnAddNew" />     
 30               <input type="button" value="获取选中数据" id="btnAddNew1"  onclick="alert($(‘select[name=duallistbox_lhdemo]‘).bootstrapDualTree(‘getSelValues‘,false).join(‘ ‘))"/>         
 31               <input type="button" value="获取选中叶子节点数据" id="btnAddNew2" onclick="alert($(‘select[name=duallistbox_lhdemo]‘).bootstrapDualTree(‘getSelValues‘).join(‘ ‘))" />         
 32               <select multiple="multiple" size="10" name="duallistbox_lhdemo2">
 33                   <!--<option value="option1">Option 1</option>-->
 34               </select>
 35               <input type="button" value="获取选中数据" id="btnAddNew3" onclick="alert($(‘select[name=duallistbox_lhdemo2]‘).bootstrapDualTree(‘getSelValues‘,false).join(‘ ‘))" />
 36               <input type="button" value="获取选中叶子节点数据" id="btnAddNew4" onclick="alert($(‘select[name=duallistbox_lhdemo2]‘).bootstrapDualTree(‘getSelValues‘).join(‘ ‘))" />   
 37           </form>
 38           <script>
 39                             
 40               //调用示例
 41               var data = {
 42                   text: "t1",
 43                   value: "v1",
 44                   pid: "0",
 45                   children: [
 46                       {
 47                           text: "t11",
 48                           value: "v11",
 49                           pid: "v1",
 50                           children: [
 51                             {
 52                                 text: "t111",
 53                                 value: "v111",
 54                                 pid: "v11",
 55                             },
 56                             {
 57                                 text: "t112",
 58                                 value: "v112",
 59                                 pid: "v11",
 60                                 children: [
 61                                     {
 62                                         text: "t1121",
 63                                         value: "v1121",
 64                                         pid: "v112",
 65                                     },
 66                                     {
 67                                         text: "t1122",
 68                                         value: "v1122",
 69                                         pid: "v112",
 70                                     },
 71                                 ],
 72                             },
 73                           ]
 74                       },
 75                       {
 76                           text: "t12",
 77                           value: "v12",
 78                           pid: "v1",
 79                           children: [
 80                             {
 81                                 text: "t121",
 82                                 value: "v121",
 83                                 pid: "v12",
 84                             },
 85                             {
 86                                 text: "t122",
 87                                 value: "v122",
 88                                 pid: "v12",
 89                             },
 90                           ]
 91                       },
 92                   ],
 93               };
 94 
 95               var lhdemo = $(select[name="duallistbox_lhdemo"]).bootstrapDualTree({
 96                   nonSelectedListLabel: 未选,
 97                   selectedListLabel: 已选,
 98                   preserveSelectionOnMove: moved,
 99                   moveOnSelect: true,
100                   //dualTree在dualListbox基础上新增的属性
101                   data: data,//树形节点数据
102                   selValues: ["v1121"], //默认选中节点值,为数组.如果不传,则默认不选中
103                   indentSymbol: "-" //缩进符号,默认为-
104               });
105               var lhdemo2 = $(select[name="duallistbox_lhdemo2"]).bootstrapDualTree({
106                   nonSelectedListLabel: 未选,
107                   selectedListLabel: 已选,
108                   preserveSelectionOnMove: moved,
109                   moveOnSelect: true,
110                   //dualTree在dualListbox基础上新增的属性
111                   data: data,//树形节点数据
112                   selValues: ["v1121", "v1122"], //默认选中节点值,为数组.如果不传,则默认不选中
113                   indentSymbol: "-" //缩进符号,默认为-
114               });
115               $("#btnAddNew").click(function () {
116                   //lhdemo.bootstrapDualTree("loadData", data, ["v1121", "v1122"]);//加载数据方法,可同时传递当前选中值
117                   lhdemo.bootstrapDualTree("setValues", ["v1121", "v1122"]);//设置当前选中值
118               });
119               
120               
121           </script>
122       </div>
123 </body>

效果如下:

技术分享

打包的bootstrap-dualtree.js文件代码如下:

  1 /**
  2 * bootstrapDualTree extended from bootstrapDualListbox
  3 * author: lh 2015-12-10
  4 */
  5 (function ($, window, document, undefined) {
  6     var pluginName = "bootstrapDualTree";//插件名称   
  7     //扩展jquery方法
  8     $.fn[pluginName] = function (options) {
  9         var returns;
 10         var args = arguments;
 11         if (options === undefined || typeof options === ‘object‘) {
 12             return this.each(function () {
 13                 if (!$.data(this, "plugin_" + pluginName)) {
 14                     $.data(this, "plugin_" + pluginName, new BootstrapDualTree(this, options));
 15                 }
 16             });
 17         } else if (typeof options === ‘string‘ && options[0] !== ‘_‘ && options !== ‘init‘) {
 18             this.each(function () {
 19                 var instance = $.data(this, ‘plugin_‘ + pluginName);
 20                 // Tests that there‘s already a plugin-instance and checks that the requested public method exists
 21                 if (instance instanceof BootstrapDualTree && typeof instance[options] === ‘function‘) {
 22                     // Call the method of our plugin instance, and pass it the supplied arguments.
 23                     returns = instance[options].apply(instance, Array.prototype.slice.call(args, 1));
 24                 }
 25             });
 26         };
 27         return returns !== undefined ? returns : this;
 28     }
 29     //定义DualTree对象
 30     function BootstrapDualTree(element, options) {
 31 
 32         var $e = $(element).bootstrapDualListbox(options);
 33         this.tElement = $e;
 34         
 35         if (options.data) {
 36             this.data = options.data;
 37         }
 38         if (options.indentSymbol!==undefined) {
 39             this.setting.indentSymbol = options.indentSymbol;
 40         }
 41         if (options.selValues) {
 42             this.selValues = options.selValues;
 43         }
 44         this.init();
 45         var dualTree = this;
 46         //bootstrap dual-listbox 在其发生变化的时候,触发change事件,实现双树都在这个事件中处理
 47         $e.change(function () {
 48             dualTree.refresh();
 49         });
 50     }
 51     //定义可对外提供的方法
 52     BootstrapDualTree.prototype = {
 53         tElement:{},//select元素
 54         data :{},//数据
 55     selValues:[],//选择的节点值    
 56     setting:{
 57         indentSymbol: "-",
 58     },
 59     lastOptions :[],//用于记录上一次的下列列表状态,以便通过比较识别移动操作的目标节点有哪些
 60         loadData: function (dataL, selValuesL) {
 61             data = dataL;
 62             selValues = selValuesL || [];
 63             this.init();
 64         },
 65         setValues: function (selValuesL) {
 66             selValues = selValuesL || [];
 67             this.init();
 68         },
 69         getSelValues: function (onlyLeaf) {
 70             if (typeof(onlyLeaf)== "undefined") onlyLeaf = true;
 71             var selValues1 = getSelValues(this.tElement,this.data, onlyLeaf);
 72             return selValues1;
 73         },
 74         init: function () {
 75             //alert(tElement)
 76             this.tElement.find("option").remove();
 77             showData(this.tElement, this.data, this.indentSymbol, 0, this.selValues);
 78             recLastOptions(this.tElement, this);
 79             this.tElement.bootstrapDualListbox("refresh");
 80             if (this.selValues.length > 0) {
 81                 updateTreeSelectedStatus(this.tElement,this,this.data, this.selValues);
 82             }
 83         },
 84         refresh: function () {
 85             updateTreeSelectedStatus(this.tElement,this, this.data);
 86         }
 87 
 88     };
 89 
 90     //获取变化事件的方向:向右选择,向左选择
 91     function getChangedDir() {
 92         var dir = "all";
 93         var srcHtml = event.srcElement.outerHTML;
 94         //arrow-right关键字针对点击箭头移动的情形,nonselected-list针对选中时直接移动的情形
 95         if (/arrow-right/.test(srcHtml) || /nonselected-list/.test(srcHtml)) {
 96             dir = "right";
 97         }
 98         else if (/arrow-left/.test(srcHtml) || /selected-list/.test(srcHtml)) {
 99             dir = "left";
100         }
101         return dir;
102     }
103     //记录上一个所有选项状态
104     function recLastOptions(tElement,tTree) {
105         tTree.lastOptions = [];
106         tElement.find("option").each(function () {
107             var curNode = $(this);
108             tTree.lastOptions.push({ value: curNode.attr("value"), selected: curNode.prop("selected") });
109         });
110     }
111     //获取发生变化的节点ID列表
112     function getChangedIds(tElement, lastOptions, dir) {
113         var changedIds = [];
114         if (dir == "right") {//向右,则取新选择的节点
115             newOptions = tElement.find("option");
116             for (var i = 0; i < newOptions.length; i++) {
117                 if (newOptions[i].selected && !lastOptions[i].selected)
118                     changedIds.push(lastOptions[i].value)
119             }
120         }
121         else if (dir == "left")//向左,则取新取消的节点
122         {
123             newOptions = tElement.find("option");
124             for (var i = 0; i < newOptions.length; i++) {
125                 if (!newOptions[i].selected && lastOptions[i].selected)
126                     changedIds.push(lastOptions[i].value)
127             }
128         }
129         return changedIds;
130     }
131 
132     //更新节点选中状态,将选中节点的父节点也都选中;
133     function updateTreeSelectedStatus(tElement, tTree, data, selValues) {
134         var dir = selValues && selValues.length > 0 ? "right" : getChangedDir();
135         var cIds = selValues || getChangedIds(tElement, tTree.lastOptions, dir);
136         console.log("changed:" + cIds)
137         if (dir == "right") {
138             //将所选节点的子节点及其路径上的节点也选中
139             for (var i = 0; i < cIds.length; i++) {
140                 var node = findNodeById(data, cIds[i]);
141                 console.log("handling-right:")
142                 console.log(node)
143                 selAllChildNodes(tElement, node);
144                 selAcesterNodesInPath(tElement,data, node);
145             }
146         }
147         else if (dir == "left") {
148             //将所选节点的子节点也都取消选中
149             for (var i = 0; i < cIds.length; i++) {
150                 var node = findNodeById(data, cIds[i]);
151                 console.log("handling-left:")
152                 console.log(node)
153                 unSelAllChildNodes(tElement, node);
154                 unSelAcesterNodesInPath(tElement,data, node);
155             }
156         }
157 
158         //重新添加未选节点及其父节点
159         //1、记录未选节点及其父节点
160         var nonSelNodes = [];
161         tElement.find("option").not(":selected").each(function () {
162             var curNode = $(this);
163             nonSelNodes.push(curNode.attr("value"));
164             while (curNode.length > 0) {
165                 var pOption = tElement.find("option[value=‘" + curNode.attr("rel") + "‘]");
166                 if (pOption.length > 0 && nonSelNodes.indexOf(pOption.attr("value")) < 0) nonSelNodes.push(pOption.attr("value"));
167                 curNode = pOption;
168             }
169         });
170         //2、清除未选择的节点
171         tElement.find("option").not(‘:selected‘).remove();
172         console.log("nonSelNodes:" + nonSelNodes)
173         //3、重新显示左侧下拉列表
174         showNonSelData(tElement, data, tTree.setting.indentSymbol, 0, nonSelNodes);
175 
176         //重新显示已选择节点,以保持排序
177         var selNodes = [];
178         makeNoDuplicateSelNode(tElement);
179         var selOptions = tElement.find("option:selected");
180         for (var n = 0; n < selOptions.length; n++)
181             selNodes.push(selOptions[n].value);
182         selOptions.remove();
183         console.log("selNodes:" + selNodes)
184         showSelData(tElement, data, tTree.setting.indentSymbol, 0, selNodes);
185 
186         tElement.bootstrapDualListbox("refresh");
187         //记录新的下拉框状态
188         recLastOptions(tElement, tTree);
189     }
190     //递归显示所有节点
191     function showData(tElement, node,indentSymbol, depth, selValues) {
192         var selValues = selValues || [];
193         var withdraw = "";
194         for (var i = 0; i < depth; i++)
195             withdraw += indentSymbol;
196         tElement.append("<option value=‘" + node.value + "‘ rel=‘" + node.pid + "‘ " + (selValues.indexOf(node.value) >= 0 ? "selected" : "") + ">" + withdraw + node.text + "</option>");
197         if (node.children) {
198             for (var n = 0; n < node.children.length; n++) {
199                 showData(tElement, node.children[n],indentSymbol, depth + 1, selValues);
200             }
201         }
202     }
203     //递归显示未选择节点
204     function showNonSelData(tElement, node, indentSymbol, depth, nonSelNodes) {
205         var withdraw = "";
206         for (var i = 0; i < depth; i++)
207             withdraw += indentSymbol;
208         if (nonSelNodes.indexOf(node.value) >= 0 && tElement.find("option[value=‘" + node.value + "‘]").not(":selected").length == 0) {
209             tElement.append("<option value=‘" + node.value + "‘ rel=‘" + node.pid + "‘>" + withdraw + node.text + "</option>");
210             if (node.children) {
211                 for (var n = 0; n < node.children.length; n++) {
212                     showNonSelData(tElement, node.children[n],indentSymbol, depth + 1, nonSelNodes);
213                 }
214             }
215         }
216     }
217     //递归显示已选择节点
218     function showSelData(tElement, node, indentSymbol, depth, selNodes) {
219         var withdraw = "";
220         for (var i = 0; i < depth; i++)
221             withdraw += indentSymbol;
222         if (selNodes.indexOf(node.value) >= 0 && tElement.find("option[value=‘" + node.value + "‘]:selected").length == 0) {
223             tElement.append("<option value=‘" + node.value + "‘ rel=‘" + node.pid + "‘ selected>" + withdraw + node.text + "</option>");
224             if (node.children) {
225                 for (var n = 0; n < node.children.length; n++) {
226                     showSelData(tElement, node.children[n], indentSymbol,depth + 1, selNodes);
227                 }
228             }
229         }
230     }
231     //去掉已选择的重复节点
232     function makeNoDuplicateSelNode(tElement) {
233         tElement.find("option:selected").each(function () {
234             var curNode = $(this);
235             var options = tElement.find("option[value=‘" + curNode.attr("value") + "‘]:selected");
236             if (options.length > 1) {
237                 for (var i = options.length; i > 0; i--)
238                     $(options[i]).remove();
239             }
240         });
241     }
242     //如果一个节点选择了,则选中其子节点
243     function selAllChildNodes(tElement, node) {
244         if (node.children) {
245             for (var n = 0; n < node.children.length; n++) {
246                 tElement.find("option[value=‘" + node.children[n].value + "‘]").prop("selected", true);
247                 selAllChildNodes(tElement, node.children[n]);
248             }
249         }
250     }
251     //如果一个节点取消选择了,则取消选中其子节点
252     function unSelAllChildNodes(tElement, node) {
253         if (node.children) {
254             for (var n = 0; n < node.children.length; n++) {
255                 tElement.find("option[value=‘" + node.children[n].value + "‘]").prop("selected", false);
256                 unSelAllChildNodes(tElement, node.children[n]);
257             }
258         }
259     }
260     //获取选中的值列表
261     function getSelValues(tElement, node, onlyLeaf) {
262         var selValuesTmp = [];
263         tElement.find("option[value=‘" + node.value + "‘]").each(function () {
264             if ($(this).prop("selected")) {
265                 if (!node.children || node.children.length == 0 || !onlyLeaf) {
266                     selValuesTmp.push(node.value);
267                 }
268                 if (node.children) {
269                     for (var n = 0; n < node.children.length; n++) {
270                         selValuesTmp = selValuesTmp.concat(getSelValues(tElement,node.children[n], onlyLeaf));
271                     }
272                 }
273             }
274         });
275         return selValuesTmp;
276     }
277     //选中一个节点的路径上的祖先节点
278     function selAcesterNodesInPath(tElement,root, node) {
279         var curNode = node;
280         while (curNode.pid != "0") {
281             curNode = findNodeById(root, curNode.pid);
282             var pOption = tElement.find("option[value=‘" + curNode.value + "‘]");
283             if (pOption.length > 0) pOption.prop("selected", true);
284         }
285     }
286     //取消一个节点的路径上的祖先节点,这些节点没有子节点被选中
287     function unSelAcesterNodesInPath(tElement, root, node) {
288         var curNode = node;
289         while (curNode.pid != "0") {
290             curNode = findNodeById(root, curNode.pid);
291             if (!hasSelChildrenNodes(tElement, curNode)) {
292                 var pOption = tElement.find("option[value=‘" + curNode.value + "‘]");
293                 if (pOption.length > 0) pOption.prop("selected", false);
294             }
295         }
296     }
297     //从树中寻找某个id的节点
298     function findNodeById(node, id) {
299         if (node.value == id) {
300             return node;
301         }
302         else {
303             if (node.children) {
304                 for (var i = 0; i < node.children.length; i++) {
305                     var rsNode = findNodeById(node.children[i], id);
306                     if (rsNode != null) return rsNode;
307                 }
308             }
309         }
310         return null;
311     }
312     //判断某个节点的子节点是否被选中
313     function hasSelChildrenNodes(tElement, node) {
314         if (node.children) {
315             for (var i = 0; i < node.children.length; i++) {
316                 var pOption = tElement.find("option[value=‘" + node.children[i].value + "‘]:selected");
317                 if (pOption.length > 0) return true;
318             }
319         }
320         return false;
321     }
322 })(jQuery, window, document);

 

BootStrap-DualListBox怎样改造成为双树

标签:

原文地址:http://www.cnblogs.com/liuhua4451/p/5056995.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!