javascript checkbox树

  研究了几天,终于搞出自己的树来。总结一下,留待以后改进用。

  作为UI控件的树与数据结构的树有很大的不同,它分为根节点,枝节点与叶节点。根节点有子树,并不隶属于其他树。枝节点有子树,并作为某一子树的节点而存在。叶节点只作为某一子树的节点而存在,并且没有子树。但仅是这样,无法绘制树的。我们看下面的图,树在网页中无论样子如何,从上到下都是一行行分开的。每一行都有几张图片与文本。这些图片大致分三类,一是虚线图标与加号图标与减号图标,我把它们统称为连线图标;二是装饰用的文件夹图标,或者根节点的那个地球,或者叶子节点的文档,或者类似的电脑、苹果、回收站等等,我称之为装饰图标;最后是checkbox,它分三种状态。图标的种类如此多,仅是根枝叶加关闭展开这两种状态,也只是六种可能,因此我们还得把枝节点细分为普通枝与末枝,叶节点也一样。由于连线图标与装饰图标的作用很相似,装饰图标好像是从视觉上强调点击连线图标会出现的效果,因此我们可以用CSS把它们整合到一起,以节省一个DOM。

  我们单独拿根节点分析。表面上,它有三个图标,一个文本,实质上,只有两个图标,第二作为第一个的背景而存在,文本位于span元素中。如果想点击文字跳转,也可以换成A元素。此外,还隐藏最后一个DIV元素。它是用于装载子树与缩进子树的。这四个元素都位于一个DIV中,我把它称之为根节点,并设置index属性,用于与原数据相比较取得父节点的index。

  
   
      var data = [
[
0,-1,"中国"],//根节点
[1,0,"北京市"],//枝节点
[2,0,"天津市"],//叶节点
[3,1,"市辖区"],//枝节点
[4,3,"东城区"],//叶节点
[5,3,"西城区"],//叶节点
[6,3,"崇文区"],//叶节点
[7,3,"朝阳区"],//叶节点
[8,3,"丰台区"]//叶节点
]

  接着我们给图标加入一些识别要素,这对样式表控制还是事件绑定都很有无用,因为仅仅是一个index是非常乏力的。当我们点击连线图标,那个加号或减号,我们怎样让程序知道是点击了它呢?!我们给它一个className,为collapse与unfold。只要有这两个类之一,我们就重设它的className,原来是collapse就改成unfold,反之亦然,然后再更换其src,以达到切换图片的目的。它后面的装饰图标是用样式表控制(内部样式),因此当className变了它会reflow,重新渲染页面。难点在checkbox,当点击它时,会有一连串反应,更换其父级节点的checkbox,更换自身,再更换其子孙。它有三种状态,对应三个className。0为子孙元素无选中,1为子孙元素全选中,2为子孙元素部分选中。算法非常复杂,渲染也非常复杂,如img为checkbox图标,它的parentNode才为树的节点元素,如果它非根节点,那它肯定还被一个DIV包裹着,img.parentNode.parentNode.parentNode才为其上级的树节点元素,这个节点的第二个IMG元素就是它顶上的那个checkbox。要访问其下级的checkbox,就更复杂了!总之,多处用到递归,自己看源码吧。由于没有学过数据结构与算法,实现有点劣拙,功能这么少也得一百五十行,惭愧万分。bug也无法自排,因此请勿用于商用,后果自负。

  
   
     var getPriorCheckbox = function(checkbox){
var node = checkbox.parentNode;//取得对应的树节点
if(/root/.test(node.className)){//判断是否根节点
return false;
}
else{
var priorNode = node.parentNode.parentNode;//取得对应的上级树节点
return priorNode.children[1];
}
}

<!doctype html> <title>树 by 司徒正美</title> <meta charset=”utf-8″/> <meta name=”keywords” content=”树 by 司徒正美” /> <meta name=”description” content=”树 by 司徒正美” /> <h2>树 by 司徒正美</h2> <mce:script type=”text/javascript”><!– Tree = function(){ this.path = “http://images.cnblogs.com/cnblogs_com/rubylouvre/205314/o_”; this.name = “tree”; this.id = + new Date + parseInt(Math.random()*100000); this.initialize.apply(this, arguments); } Tree.prototype = { constructor : Tree, initialize : function(config){ var me = this, renderTo = config.renderTo; me.tree = config.data; me.container = ((typeof renderTo === “string”) ? document.getElementById(renderTo) : renderTo) || document.body; me.panel = document.createElement(“div”); me.panel.setAttribute(“id”,”tree” + me.id) me.container.insertBefore(me.panel,null); var id = “#”+me.panel.id; var sheet = document.createElement(‘style’); sheet.type = ‘text/css’; document.getElementsByTagName(‘head’)[0].appendChild(sheet); var bg = “background:transparent url(“+me.path; var cssCode = id +” {font-size:12px;}\n” + id+” img {border:0;vertical-align: middle;}\n” + id+” span {vertical-align: bottom;}\n”+ id+” .line {padding-left:18px;”+bg+”line.gif) repeat-y 0 0;}\n”+ id+” .blank {margin-left:18px;}\n”+ id+” img.collapse {padding-right:18px;”+bg+”folder.gif) no-repeat right center;}\n”+//控制枝节点的装饰图标(闭合) id+” img.unfold {padding-right:18px;”+bg+”folderopen.gif) no-repeat right center;}\n”+//控制枝节点的装饰图标(展开) id+” img.root {padding-right:18px;”+bg+”root.gif) no-repeat right center;}\n”+ //控制根节点的装饰图标 id+” img.leaf {padding-right:18px;”+bg+”leaf.gif) no-repeat right center;}\n” //控制叶节点的装饰图标 if(!+”\v1″){ sheet.styleSheet.cssText = cssCode }else if(/a/[-1]==’a’){ sheet.innerHTML = cssCode }else{ sheet.appendChild(document.createTextNode(cssCode)); } //添加根节点 var icon = me.makeImage(“nolines_plus”,”collapse root”) var checkbox0 = me.makeImage(“checkbox_0″,”checkbox_0″); me.panel.innerHTML = me.makeTree(me.tree[0][0],”b”,0,icon+checkbox0, me.tree[0][2]); me.childs = []; me.checks = [];//由来装载点击数 me.panel.onclick = function(e){ e = e || window.event; var node = e.srcElement ? e.srcElement : e.target; var current = node.parentNode; var currentIndex = current.getAttribute(“index”); var currentPrefix = current.getAttribute(“prefix”); var currentLevel = current.getAttribute(“level”); var subtree = me.getSubtree(currentIndex); var children = current.children[3]; //添加子树集合 //存在子树并且没有添加时才添加 if(subtree && !children){ children = document.createElement(“div”); var childs = []; for(var i=0,length = subtree.length;i<length;i++){ var isLimb = me.hasSubtree(subtree[i][0]); var isLast = (i == subtree.length – 1); var prefix = isLast ? “blank” : “line”; icon = isLast ? “plusbottom” : “plus”; if(isLimb){ icon = me.makeImage(icon,”collapse limb”);//枝节点前面的装饰图标 }else{ icon = icon.replace(/plus/, “join”);//叶子节点前面的连线图标 icon = me.makeImage(icon,”leaf”);//叶子节点前面的装饰图标 } childs.push(subtree[i][0]) children.innerHTML += me.makeTree(subtree[i][0],prefix,+currentLevel+1,icon+checkbox0,subtree[i][2]) } me.childs[currentIndex] = childs; children.className = (currentPrefix == “line”) ? “line”: “blank”; current.insertBefore(children,null); } if(/collapse/.test(node.className)){//如果点击是加号或减号图标 node.src = node.src.replace(/plus/,”minus”);//改变连线图标 node.className = node.className.replace(“collapse”,”unfold”);//改变装饰图标 children &&(children.style.display = “block”); }else if(/unfold/.test(node.className)){ node.src = node.src.replace(/minus/,”plus”);//改变连线图标 node.className = node.className.replace(“unfold”,”collapse”);//改变装饰图标 children &&(children.style.display = “none”); } if(/checkbox/.test(node.className)){//如果单击的是checkbox图标 var checked = me.isChecked(node);//如果是true则–,如果是false则++ me.setPriorCheckbox(current,checked);//一开始肯定是checkbox,返回false me.setJuniorCheckbox(current,checked) } } }, setJuniorCheckbox : function(node,/*Boolean*/checked){ var checkbox = node.children[1]; var replaceCheckbox = checked ? “checkbox_0″ :”checkbox_1”; checkbox.src = checkbox.src.replace(/checkbox_\d/,replaceCheckbox); checkbox.className = replaceCheckbox; var index = node.getAttribute(“index”); if(!!this.childs[index]){ var length = this.childs[index].length; this.checks[index] = checked ? length : 0; if(length > 0){ var children = node.children[3].children; while(–length >= 0){ this.setJuniorCheckbox(children[length],checked) } } } }, setPriorCheckbox :function(node,/*Boolean*/checked){//设置上一级树的checkbox var index = node.getAttribute(“index”); var prior = node.parentNode.parentNode; var priorIndex = this.tree[index][1]; var priorCheckbox = prior.children[1]; var priorLevel = prior.getAttribute(“level”); var priorCount = this.checks[priorIndex] || 0; checked ? priorCount– : priorCount++; (priorCount < 0) && (priorCount = 0) this.checks[priorIndex] = priorCount; if(!!priorCheckbox){ //当priorIndex等于-1时, //priorCheckbox不存在 //me.childs[priorIndex]为undefined,不存在长度 checked = (priorCount == this.childs[priorIndex].length); var replaceCheckbox = checked ? “checkbox_1” : “checkbox_2″; //checkbox_1为全选,checkbox_2为非全选 //全选,则让上级++,即让checked为false priorCheckbox.src = priorCheckbox.src.replace(/checkbox_\d/,replaceCheckbox);//???????? priorCheckbox.className = replaceCheckbox; } if(priorLevel > 0){ //根节点没有priorCheckbox,且priorLevel等于-1 this.setPriorCheckbox(prior,checked); } }, isChecked : function(node){//如果是checkbox_0返回false,checkbox_1与checkbox_2返回true return node.src.slice(-5,-4) > 0; }, makeImage : function(image){ var status =””; if(arguments[1] != null){ status = “class='” + arguments[1] +”‘”; } return “<img src=””+this.path+image+”.gif” mce_src=”/admin/doc/”+this.path+image+”.gif” “+status+” />” }, makeTree : function(index,prefix,level,images,text){ var builder = []; builder.push(“<div index='”); builder.push(index); builder.push(“‘ prefix='”) builder.push(prefix); builder.push(“‘ level='”) builder.push(level); builder.push(“‘>”); builder.push(images); builder.push(“<span>”); builder.push(text); builder.push(“</span></div>”) return builder.join(”); }, hasSubtree : function (p){ var tree = this.tree; for(var i = 0,length = tree.length;i < length; i++){ if(this.tree[i][1] == p){ return true; }; }; return false; }, getSubtree : function (p){ var subtree = [],tree = this.tree; for(var i = 0,length = tree.length;i < length; i++){ if(tree[i][1] == p){ subtree.push(tree[i]); }; }; return subtree } } var data = [ [0,-1,”前台技术”], [1,0,”表现层”], [2,1,”CSS”], [3,2,”CSS资源”], [4,2,”CSS3前瞻”], [5,1,”web标准知识”], [6,1,”图形”], [7,6,”SVG”], [8,6,”VML”], [9,6,”canvas”], [10,0,”结构层”], [11,10,”HTML”], [12,10,”微格式”], [13,10,”XML”], [14,13,”XPath”], [15,0,”行为层”], [16,15,”core”], [17,16,”变量与参数”], [18,16,”对象与继承”], [19,16,”函数与闭包”], [20,16,”算法”], [21,16,”高级技术”], [22,21,”跨域请求”], [23,21,”提速技术”], [24,21,”本地存储”], [25,21,”函数劫持”], [26,16,”框架设计”], [27,26,”Ext”], [28,26,”dojo”], [29,26,”mootools”], [30,15,”Ajax”], [31,15,”DOM”], [32,15,”BOM”] ] window.onload = function(){ var tree = new Tree({renderTo:”test”,data:data}); } // –></mce:script> <div id=”test”></div>

运行代码

千百度
© 版权声明
THE END
喜欢就支持一下吧
点赞8 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容