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

我们单独拿根节点分析。表面上,它有三个图标,一个文本,实质上,只有两个图标,第二作为第一个的背景而存在,文本位于span元素中。如果想点击文字跳转,也可以换成A元素。此外,还隐藏最后一个DIV元素。它是用于装载子树与缩进子树的。这四个元素都位于一个DIV中,我把它称之为根节点,并设置index属性,用于与原数据相比较取得父节点的index。
<div class= "cnblogs_code" ><pre><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http: //www.CodeHighlighter.com/<br /><br />--><span style="color: #000000;"> </span><span style="color: #0000FF;">var</span><span style="color: #000000;"> data </span><span style="color: #000000;">=</span><span style="color: #000000;"> [<br> [</span><span style="color: #000000;">0</span><span style="color: #000000;">,</span><span style="color: #000000;">-</span><span style="color: #000000;">1</span><span style="color: #000000;">,</span><span style="color: #000000;">"</span><span style="color: #000000;">中国</span><span style="color: #000000;">"</span><span style="color: #000000;">],</span><span style="color: #008000;">//</span><span style="color: #008000;">根节点</span><span style="color: #008000;"><br></span><span style="color: #000000;"> [</span><span style="color: #000000;">1</span><span style="color: #000000;">,</span><span style="color: #000000;">0</span><span style="color: #000000;">,</span><span style="color: #000000;">"</span><span style="color: #000000;">北京市</span><span style="color: #000000;">"</span><span style="color: #000000;">],</span><span style="color: #008000;">//</span><span style="color: #008000;">枝节点</span><span style="color: #008000;"><br></span><span style="color: #000000;"> [</span><span style="color: #000000;">2</span><span style="color: #000000;">,</span><span style="color: #000000;">0</span><span style="color: #000000;">,</span><span style="color: #000000;">"</span><span style="color: #000000;">天津市</span><span style="color: #000000;">"</span><span style="color: #000000;">],</span><span style="color: #008000;">//</span><span style="color: #008000;">叶节点</span><span style="color: #008000;"><br></span><span style="color: #000000;"> [</span><span style="color: #000000;">3</span><span style="color: #000000;">,</span><span style="color: #000000;">1</span><span style="color: #000000;">,</span><span style="color: #000000;">"</span><span style="color: #000000;">市辖区</span><span style="color: #000000;">"</span><span style="color: #000000;">],</span><span style="color: #008000;">//</span><span style="color: #008000;">枝节点</span><span style="color: #008000;"><br></span><span style="color: #000000;"> [</span><span style="color: #000000;">4</span><span style="color: #000000;">,</span><span style="color: #000000;">3</span><span style="color: #000000;">,</span><span style="color: #000000;">"</span><span style="color: #000000;">东城区</span><span style="color: #000000;">"</span><span style="color: #000000;">],</span><span style="color: #008000;">//</span><span style="color: #008000;">叶节点</span><span style="color: #008000;"><br></span><span style="color: #000000;"> [</span><span style="color: #000000;">5</span><span style="color: #000000;">,</span><span style="color: #000000;">3</span><span style="color: #000000;">,</span><span style="color: #000000;">"</span><span style="color: #000000;">西城区</span><span style="color: #000000;">"</span><span style="color: #000000;">],</span><span style="color: #008000;">//</span><span style="color: #008000;">叶节点</span><span style="color: #008000;"><br></span><span style="color: #000000;"> [</span><span style="color: #000000;">6</span><span style="color: #000000;">,</span><span style="color: #000000;">3</span><span style="color: #000000;">,</span><span style="color: #000000;">"</span><span style="color: #000000;">崇文区</span><span style="color: #000000;">"</span><span style="color: #000000;">],</span><span style="color: #008000;">//</span><span style="color: #008000;">叶节点</span><span style="color: #008000;"><br></span><span style="color: #000000;"> [</span><span style="color: #000000;">7</span><span style="color: #000000;">,</span><span style="color: #000000;">3</span><span style="color: #000000;">,</span><span style="color: #000000;">"</span><span style="color: #000000;">朝阳区</span><span style="color: #000000;">"</span><span style="color: #000000;">],</span><span style="color: #008000;">//</span><span style="color: #008000;">叶节点</span><span style="color: #008000;"><br></span><span style="color: #000000;"> [</span><span style="color: #000000;">8</span><span style="color: #000000;">,</span><span style="color: #000000;">3</span><span style="color: #000000;">,</span><span style="color: #000000;">"</span><span style="color: #000000;">丰台区</span><span style="color: #000000;">"</span><span style="color: #000000;">]</span><span style="color: #008000;">//</span><span style="color: #008000;">叶节点</span><span style="color: #008000;"><br></span><span style="color: #000000;"> ]<br></span></pre> </div> |
接着我们给图标加入一些识别要素,这对样式表控制还是事件绑定都很有无用,因为仅仅是一个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也无法自排,因此请勿用于商用,后果自负。
<div class= "cnblogs_code" ><pre><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http: //www.CodeHighlighter.com/<br /><br />--><span style="color: #000000;"> </span><span style="color: #0000FF;">var</span><span style="color: #000000;"> getPriorCheckbox </span><span style="color: #000000;">=</span><span style="color: #000000;"> </span><span style="color: #0000FF;">function</span><span style="color: #000000;">(checkbox){<br> </span><span style="color: #0000FF;">var</span><span style="color: #000000;"> node </span><span style="color: #000000;">=</span><span style="color: #000000;"> checkbox.parentNode;</span><span style="color: #008000;">//</span><span style="color: #008000;">取得对应的树节点</span><span style="color: #008000;"><br></span><span style="color: #000000;"> </span><span style="color: #0000FF;">if</span><span style="color: #000000;">(</span><span style="color: #000000;">/</span><span style="color: #000000;">root</span><span style="color: #000000;">/</span><span style="color: #000000;">.test(node.className)){</span><span style="color: #008000;">//</span><span style="color: #008000;">判断是否根节点</span><span style="color: #008000;"><br></span><span style="color: #000000;"> </span><span style="color: #0000FF;">return</span><span style="color: #000000;"> </span><span style="color: #0000FF;">false</span><span style="color: #000000;">;<br> }</span><span style="color: #0000FF;">else</span><span style="color: #000000;">{<br> </span><span style="color: #0000FF;">var</span><span style="color: #000000;"> priorNode </span><span style="color: #000000;">=</span><span style="color: #000000;"> node.parentNode.parentNode;</span><span style="color: #008000;">//</span><span style="color: #008000;">取得对应的上级树节点</span><span style="color: #008000;"><br></span><span style="color: #000000;"> </span><span style="color: #0000FF;">return</span><span style="color: #000000;"> priorNode.children[</span><span style="color: #000000;">1</span><span style="color: #000000;">];<br> }<br> }<br></span></pre> </div> |