一、User Story
新的项目中有严格的域控制需求,其实就是要实现一个无限极的树结构,其实以前实现过各种树形结构,什么菜单、权限树、产品目录,但是这次的无限级伸 展的可控制树结构着实难到我了,光是找方法就找了很长时间,以前的级别都是死的,而且单纯的checkbox级联遍历都非常的慢。
二、解决方案的寻找
A.考虑过xtree,但是反复考虑和看sample,发现这个东西还是不好控制,特别是它的代码非常不好懂,如果要是扩展起来也非常难(上个项目中曾经有同事用过,超级慢不说还很难扩展,他修改的代码我死活看不懂)。
B.自己去实现,发现这个更难,以前实现3层的树还好,但是代码已经很壮观,而且JSP里面掺杂Java代码和JS代码,简直是失败中的失败……而网上的 例子很多,但是好用的其实并不多,特别是代码会有不兼容Firefox的问题(我是Ubuntu用户)。后来还有朋友传给我他实现的Ajax加载树,但是 发现仍然比较难懂,而且扩展性也不是很好,愁!
最终选定YUI的checktree,真是有种踏破铁鞋无觅处的感觉,YUI实现了种类繁多的JavaScript应用基础类包,可以方便的扩展使用,因 为代码质量比较高的缘故(代码清晰、文档较全),扩展起来也相对容易些,但是YUI复杂的命名空间和大量的继承也令人很苦恼的说。在实现这棵树以后,还大 量使用其中的Ajax支持包connection,真是相当舒服,感觉已经超过Prototype,还有就是YUI还提供的已经压好的代码包,很贴心 ^^。
三、实现 YUI checktree的改造
说了不少废话(实际上前面这个过程我用了两天的时间 T_T),现在说说实际功能,由于需要为这棵树提供多种多样的附加功能,所以代码比较大,就放在附件里面,这里写点关键的实现部分。
1.修改源代码 YUI checktree继承自treeview控件,需要一个叫TaskNode.js的类支持,TaskNode.js继承了 Yohoo.widget.TextNode,是YUI default tree,是采用Div内嵌table实现的,代码写的比较精巧,全是面向对象的实现方法,得益于YUI良好的继承机制,而TaskNode更爽,点击后 的级联效果做的非常漂亮,而且速度惊人的快,完全没有一般checkbox树遍历的慢速,原因是它的所有checkbox都是图片,然后采用css来控 制,妙哉!(其实也有相应的问题,这样的树不大好做disable,一个是图片不好搞,还有就是屏蔽它的onclick方法都不大好改,我目前还不知道怎 么实现,太忙了最近。)
说修改源代码其实只修改了两个地方,目的是让树结构能多带一些关键数据(原有的树index是子增长的,修改后可以描述主键)。
A.修改 treeview.js,修改init: function(oData, oParent, expanded)方法,改为init: function(oData, oParent, expanded,oIndex),然后this.index=oIndex,这样在初始化树的时候节点的index可以放一个对象的主键进去,达到区分的效果。
B.修改TaskNode.js,也就是YAHOO.widget.TaskNode的构造函数增加一个参数,调用上面改的init方法就可以构造一个带有对象主键和名字的树结构。这样构造一个根节点的方法:var node = YAHOO.widget.TaskNode("name",tree.getRoot(),false,false,"id"),很爽
2.接下来比较麻烦的就是构造树结构,从数据库取出来的数据如何以属性结构展现着实令人难以琢磨,为了这个树结构起初打算使用XML来解析,但是发现构造数据和解析仍然是很难过。
其实展开树在现在看来还是比较固定的方法:把数据分层,一层一层构造节点。
然后是构造一棵树的代码:
js 代码
- tree = new YAHOO.widget.TreeView(treeDiv);
- for(j=0;j
- var domain = domainList[j];
- if(j==0){
- eval("var node" + domain.domainId + "_" + domain.levelId + "= new YAHOO.widget.TaskNode(domain.name,tree.getRoot(), false, false, domain.domainId);");
- }else{
- var levelId = parseInt(domain.levelId) - 1;
- try{
- eval("var node" + domain.domainId + "_" + domain.levelId + "= new YAHOO.widget.TaskNode(domain.name,eval(node" + domain.domDomainId + "_" + levelId + "), false, false, domain.domainId);");
- }catch(e){
- eval("var node" + domain.domainId + "_" + domain.levelId + "= new YAHOO.widget.TaskNode(domain.name,tree.getRoot(), false, false, domain.domainId);");
- }
- }
- }
domainList的结构是一个用JSON转化过来的Java对象数组,具体结构我会在另一篇日志里面记录,这里domain包括domainId、name、parentId、leveleId几个基本的数据。
比较复杂一点的地方是动态构造节点,因为所有的节点都是在遍历结构集后生成的,每个节点如果没有父节点(这里用的levelId判断),就把 tree.getRoot()这个默认的根节点做为父节点,其他节点只要根据eval("var node" + domain.domainId + "_" + domain.levelId)的结果找到父对象就可以了。另外里面包含了try catch代码,就是应对树结构不够完整的情况,如果某节点层次并不是最低,但是却没有父节点,就把它直接加到根节点上做一个新的根节点,不过这个try catch可能会有隐患,还在考虑其他的解决办法中……
至此一颗区域树已经构造完毕
3.一些基本的获取数据方法。用到这棵树最常见的就是获取当前状态下选择的值,没有什么好的办法,只能递归,想法参照YUI checktree的Sample,它是用一个递归的循环随机生成4层结构的树,那么在处理这棵树的结构中几乎都是使用递归来完成的,目前不保证数据量十 分大的时候速度会有影响,但是JavaScript本来也不是省油的灯,好在这种区域也就是千百个的,性能影响不那么明显。
A.取得所有选择的值,该checktree规定每个节点都有一个checkstate,(0=unchecked, 1=some children checked, 2=all children checked),这就给我们递归带来很大方便,如果你遍历到某个节点checkstate是2,那么立马知道以下节点都被选中,拿到这个值就足够,大大 减少递归次数。所以取得选择结果的方法:
js 代码
- var checkResult = new Array(0);
- var topNodes = tree.getRoot().children;
- getResult(topNodes, checkResult);
-
- function getResult(nodes,checkResult){
- for(var i=0; i
- var node = nodes[i];
- var checkState = node.checkState;
- if(checkState == 0){
- continue;
- }else if (checkState == 2){
- checkResult.push(node.index);
- }else if (checkState == 1){
- var childNodes = node.children;
- if(childNodes && childNodes.length>0){
- getResult(childNodes, checkResult);
- }
- }
- }
- }
这样返回一个checkResult数组。
B.默认值选择,一般用于更新树结构的情况,会有一些节点默认被选中,同样也是一个递归就可以
javascript 代码
- var domains = new Array();
- if(checkResult.indexOf(";") > 0){
- domains = checkResult.split(";");
- } else {
- domains.push(checkResult);
- }
- for(var i = 0; i < domains.length; i++){
- var domainId = domains[i];
- tree.getNodeByIndex(domainId).check();
- }
其中传入一个参数checkResult,结构为"1;2;4;5"这样的字串。
C.构建一个示例,接下来就是看看实际效果,由于现在的区域树里面做了一些信息的描述,为了操作方便,采用JSON进行了封装,后台构造好的POJO直接转化为JSON对象拿到前台来用,然后测试代码这样写:
js 代码
- <script>"text/javascript">
- var testData = '[{"levelId":"1","domDomainId":"root","domainId":0,"coldId":"086","name":"中国"},{"levelId":"2","domDomainId":0,"domainId":2,"coldId":"021","name":"上海"},{"levelId":"2","domDomainId":0,"domainId":57,"coldId":"010","name":"北京"},{"levelId":"2","domDomainId":0,"domainId":6,"coldId":"020","name":"广东"},{"levelId":"3","domDomainId":2,"domainId":3,"coldId":"001","name":"浦东"},{"levelId":"3","domDomainId":6,"domainId":8,"coldId":"002","name":"深圳"},{"levelId":"3","domDomainId":6,"domainId":9,"coldId":"002","name":"珠海"},{"levelId":"3","domDomainId":6,"domainId":7,"coldId":"001","name":"广州"},{"levelId":"3","domDomainId":2,"domainId":63,"coldId":"005","name":"徐汇区"},{"levelId":"3","domDomainId":2,"domainId":61,"coldId":"015","name":"闽行区"}]';
- var domainList = eval(testData);
- buildCheckTree("treeDiv1", domainList, false);
- </script>
上面的测试代码中采用JSON数据,是由Ajax请求服务器端产生的结果转化为JSON对象响应给客户端,具体实现方法参照我的另一篇日志:http://www.iteye.com/topic/91077。
四、结语
至此,一个复杂的区域的树结构就完成了,可以做为一种选择方案,YUI checktree的好处在于它的checkbox实现机制不同于以往的方法,采用css图片控制使得复杂而缓慢的遍历变得轻松畅快。在附件中有形成的效果截图和代码包,myCheckTree.js是我根据项目需求写的方法,里面提供了多种获得树结构数据选择结果的方法。如果想生成没有checkbox的话也很简单,把方法名调用 改为buildTree就可以。实现了对这棵区域树的基本操作和数据传递,数据采用Ajax传递至页面,后台使用JSON封装POJO,具体内容有时间将 在下一篇日志里面记录。很多方法还没有仔细测试过,欢迎大家指正!
- 描述: 初始树演示图片
- 大小: 12.8 KB
- 描述: 选择结果演示图片
- 大小: 20.7 KB
分享到:
相关推荐
NULL 博文链接:https://ttwang.iteye.com/blog/1741631
使用YUI2.8 仿照JQuery UI 的select插件写的一个函数 详情见我的博客http://hi.baidu.com/hjzheng
这是对YUI拖拽例子的改编,大家可以去访问的博客 http://hi.baidu.com/hjzheng
YUI 是 jquery 一样网页使用的javascrpit 方法库。
基于yui的layout,tree的一个demo
YUI教程YUI 入门教程YUI 入门教程YUI 入门教程
dwr-yui实现分页,dwr-yui实现分页,dwr-yui实现分页
YUI-EXT使用详解,免费送给大家
NULL 博文链接:https://ttwang.iteye.com/blog/1741592
JavaScript是一种最初由Netscape的LiveScript发展而来的面向对象的Web脚本语言,被ECMA国际定义为国际化标准——ECMAScript。JavaScript具有使用局限性。... 本书适用于YUI工程师,也可以作为教材供高校师生学习使用。
利用Yahoo YUI库做的一个TREE实例,很详细的阐述了YUI的使用原理
--charset <charset> 指定读取输入文件使用的编码 --line-break <column> 在指定的列后插入一个 line-bread 符号 -v, --verbose 显示info和warn级别的信息 -o <file> 指定输出文件。默认输出是控制台。 ...
YUI的使用文档,汉语版的,个人日记,是别人写的,不是我写的
yui 源码下载,3.9.0 r2 包,最新版本
alloy_Liferay使用YUI封装的框架,含使用文档
YUI 库,全称Yahoo! UI Library。是一组工具和控件,用...YUI 基于BSD协议,对所有的使用方式都是免费的。YUI 项目包括YUI 库和两个创建时工具: YUI Compressor (压缩) 和 YUI Doc (JavaScripts代码的文档引擎)。
yui3-master.zip
YUI Test is a complete testing framework for JavaScript and Web applications. You can use the simple JavaScript syntax to write unit tests that can be run in web browsers or on the command line, as ...
可惜官网提供的版本都不具备右键功能,每次压缩都要cmd输入一些命令实在是繁琐,本文就介绍如何给YUI Compressor添加右键命令,方便使用。 网上已有一些网友写好的安装包,但对应的版本太低,目前最新最多人使用的...
yuicompressor-maven-plugin, 用于压缩 (Minify/Ofuscate/Aggregate) Javascript文件和使用 YUI 压缩器的CSS文件的Maven 插件 [[Flattr this git repo] ( http://api.flattr.com/button/flattr-badge-large.png)]...