對於Javascript中checkbox樹的功能,扥多人還不是很瞭解,究竟checkbox樹有何作用,希望本文能對大家有所協助。研究了幾天,終於搞出自己的樹來。總結一下,留待以後改進用。
作為UI控制項的樹與資料結構的樹有很大的不同,它分為根節點,枝節點與分葉節點。根節點有子樹,並不隸屬於其他樹。枝節點有子樹,並作為某一子樹的節點而存在。分葉節點只作為某一子樹的節點而存在,並且沒有子樹。但僅是這樣,無法繪製樹的。我們看下面的圖,樹在網頁中無論樣子如何,從上到下都是一行行分開的。每一行都有幾張圖片與文本。這些圖片大致分三類,一是虛線表徵圖與加號表徵圖與減號表徵圖,我把它們統稱為連線表徵圖;二是裝飾用的檔案夾表徵圖,或者根節點的那個地球,或者葉子節點的文檔,或者類似的電腦、蘋果、資源回收筒等等,我稱之為裝飾表徵圖;最後是checkbox,它分三種狀態。表徵圖的種類如此多,僅是根枝葉加關閉展開這兩種狀態,也只是六種可能,因此我們還得把枝節點細分為普通枝與末枝,分葉節點也一樣。由於連線表徵圖與裝飾表徵圖的作用很相似,裝飾表徵圖好像是從視覺上強調點擊連線表徵圖會出現的效果,因此我們可以用CSS把它們整合到一起,以節省一個DOM。
我們單獨拿根節點分析。表面上,它有三個表徵圖,一個文本,實質上,只有兩個表徵圖,第二作為第一個的背景而存在,文本位於span元素中。如果想點擊文字跳轉,也可以換成A元素。此外,還隱藏最後一個DIV元素。它是用於裝載子樹與縮排子樹的。這四個元素都位於一個DIV中,我把它稱之為根節點,並設定index屬性,用於與原資料相比較取得父節點的index。
01.
var
data = [
02.
[0,-1,
"中國"
],
//根節點
03.
[1,0,
"北京市"
],
//枝節點
04.
[2,0,
"天津市"
],
//分葉節點
05.
[3,1,
"市轄區"
],
//枝節點
06.
[4,3,
"東城區"
],
//分葉節點
07.
[5,3,
"西城區"
],
//分葉節點
08.
[6,3,
"崇文區"
],
//分葉節點
09.
[7,3,
"朝陽區"
],
//分葉節點
10.
[8,3,
"丰台區"
]
//分葉節點
11.
]
接著我們給表徵圖加入一些識別要素,這對樣式表控制還是事件綁定都很有無用,因為僅僅是一個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><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' "+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}); }</script><div id="test"></div>
原文標題:javascript checkbox樹
連結:http://www.cnblogs.com/rubylouvre/archive/2009/10/26/1589692.html
- 淺析利用Javascript擷取隨機顏色
- JSON是什嗎?為JavaScript準備的資料格式
- 十個最常用的JavaScript自訂函數
- 有關JavaScript事件載入的一些延伸思考
- JavaScript使用心得匯總:從BOM和DOM談起