在網頁開發中,大部分的下拉式功能表都是一行一項並且上下對齊,這樣雖然很好但是缺乏階層感,客戶不知道各個選項之間的關係,因此有的時候我們需要在下拉式功能表中以樹形結構展示下拉項,給客戶更好的體驗。比如:假設資料庫中有張表存放了中國省市資訊,如下所示:
這張表是符合樹形結構特徵的,它的樹形結構如右圖所示,相信很多表都有這樣的特徵,這種結構的表以下拉樹展示最好不過了,使用者可以直接看出選項之間的關係。
javascript實現樹形結構應該有很多種方法,我也不知道那些方法都是如何?的,也不想從網上下載現成的,因此決定自己寫一個,剛開始沒什麼頭緒,後來突然靈光一現,想到了數組鏈表的方法,大概思路如下,先上圖:
<img width="" height="" " src="http://img.ddvip.com/2014/0819/201408190841349030.jpg" />
上面這幅圖是樹形結構在數組中的儲存形式,每個節點以對象的形式存放在數組中,每個對象都有一個“array”屬性,它是一個數組,指向它的所有子節點,"array"非常重要,樹形結構的所有操作都要用到它,特別是對樹的遍曆,可以說是這個屬性使多個數組組合起來形成一棵樹,只不過這棵樹是在記憶體中是無形的,我把它理解為“抽象樹(我只是瞎說啊,僅是個人叫法,沒有任何根據)”,是不是有點數組鏈表的味道啊?為了把它變為有形的樹,我們可以對它進行遍曆,在遍曆的時候產生適當的html標籤,最後產生樹的完整html代碼。
下面介紹一下實現過程:
首先,把資料庫中的表以對象的形式儲存在數組裡面,怎麼儲存我就不說了,不是本文重點,為了簡單我直接定義好一個數組,代碼如下:
//為了方便處理,表中每一行的資訊以對象的形式存放在數組中var array = [ {id: 0,parentId: -1,name: "China" },{id: 1, parentId:0,name: "HeNan"},{id: 2, parentId:1, name:"LuoYang"}, {id: 3,parentId: 1,name: "ZhengZhou" },{id: 4,parentId: 0,name: "HeBei" },{id: 5,parentId: 4,name: "TangShang" }, {id: 6,parentId: 4,name: "ShiJiangZhuang" },{id: 7,parentId: 0,name: "HeiLongJiang" },{id: 8,parentId: 7,name: "Haerbin" }, {id: 9,parentId: 7,name: "DaQing" },{id: 10,parentId: 0,name: "GuangDong" },{id: 11,parentId: 10,name: "GuangZhou" }, {id: 12,parentId: 10,name: "FoShan" },{id:13,parentId:1,name:"KaiFeng"},{id:14, parentId: 2, name:"RuYang"}, {id:15,parentId:0,name:"meself"},{id:16,parentId:-1,name:"America"},{id:17,parentId:16,name:"Maiami"}, {id:18,parentId:17,name:"Heat"} ];
然後,對這個數組進行處理,給每個對象添加“level”屬性,為了方便處理把數組按照對象的“level”屬性從小到大進行排序,並給數組中每個對象添加最重要的"array”屬性,然後遍曆樹產生樹的html,代碼如下:
//給數組中的對象添加"level"屬性 this.initLevel = function(){ for(var i=0; i<this.array.length;i++) { this.level = 0; //計算每個節點的層次之前,先重設為0 this.getLevel(this.array[i].id); this.array[i].level = this.level; } } //計算每個節點的層次this.getLevel = function (id) { if(!Boolean(this.array[id])||id==-1) { //alert("The id "+ id +" is not included in array!"); return false; } if(this.array[id].id!=-1) { this.level = this.level + 1; //var iidd = this.array[id].parentId; //arguments.callee(iidd); this.getLevel(this.array[id].parentId); } } //初始化抽象樹,處理根節點(支援多個根節點的情況,即:多棵樹)//查看本欄目更多精彩內容:http://www.bianceng.cnhttp://www.bianceng.cn/webkf/script/ this.initAbstructTree = function () { var rootCount = 0; //array_1為全域變數 array_1 = new Array(1); //shaobing = shaobing + 1; for(var i=0;i<this.array.length;i++) { if(this.array[i].level==1) { rootCount++; array_1[i] = this.array[i]; //shaobing = shaobing + 1; } else break; } } //建立抽象樹,用遞迴的方式處理根結點以外的所有節點,以數組的形式建立樹形結構 this.createAbstructTree = function (arrayn) { //儲存現在正在遍曆array數組中的第幾層 var layer = arrayn[0].level + 1; if(layer>this.array[this.array.length-1].level) return; for(var i=0;i<arrayn.length;i++) { for(var j=0;j<this.array.length;j++) { if(this.array[j].level>layer) break; if(this.array[j].level == layer&&this.array[j].parentId == arrayn[i].id) { if(!Boolean(arrayn[i].array)) { arrayn[i].array = new Array(); } arrayn[i].array.push(this.array[j]); } } if(Boolean(arrayn[i].array)) { this.createAbstructTree(arrayn[i].array); } } } //按照數組中儲存的抽象樹形結構,遞迴產生樹形結構的html this.generateTreeHtml = function (arrayn,signalArray) { for(var i=0;i<arrayn.length;i++) { if(arrayn[i].level == 1) //根節點 { if(i == (arrayn.length-1)) //第一層的最後一個節點的表徵圖不一樣 { this.html += "<div><div id='level"+arrayn[i].level+"_"+arrayn[i].id+"'><img src=\"images/Lminus.png\"><img src=\"images/openfoldericon.png\"><span>"+arrayn[i].name+"</span></div>"; } else this.html += "<div><div id='level"+arrayn[i].level+"_"+arrayn[i].id+"'><img src=\"images/Tminus.png\"><img src=\"images/openfoldericon.png\"><span>"+arrayn[i].name+"</span></div>"; //控制標籤的顯示,如果有子節點,則將所有的子節點放在<div>標籤裡面 if(Boolean(arrayn[i].array)) { this.html += "<div>" } if(i == (arrayn.length-1)) { if(Boolean(arrayn[i].array)) //有子節點 { signalArray.push("blank"); this.generateTreeHtml(arrayn[i].array,signalArray); signalArray.pop(); } } else { if(Boolean(arrayn[i].array)) //有子節點 { signalArray.push("I"); this.generateTreeHtml(arrayn[i].array,signalArray); signalArray.pop(); } } if(Boolean(arrayn[i].array)) { this.html += "</div></div>"; //結束標籤 } else this.html += "</div>"; //結束標籤 } else //非根節點 { //添加對應的外層圖片 var outerbiaoqian=""; var innerbiaoqian=""; if(signalArray.length>0) { for(var k=0; k<signalArray.length;k++) { outerbiaoqian += "<img src='images/"+signalArray[k]+".png'>" } } //添加自己的圖片 if(i == arrayn.length-1) //最後一個節點 { if(Boolean(arrayn[i].array)) { //並且有子節點 innerbiaoqian+="<img src='images/Lminus.png'><img src='images/openfoldericon.png'>"; //signalArray.push("blank"); } else { //沒有子節點 innerbiaoqian+="<img src='images/L.png'>"; //signalArray.push("blank"); } } else //不是最後一個節點 { if(Boolean(arrayn[i].array)) { //並且有子節點 innerbiaoqian+="<img src='images/Tminus.png'><img src='images/openfoldericon.png'>"; } else { //沒有子節點 innerbiaoqian+="<img src='images/T.png'>"; } } if(Boolean(arrayn[i].array)) //有子節點 { this.html += "<div id='level"+arrayn[i].level+"_"+arrayn[i].id+"'>"+outerbiaoqian+innerbiaoqian+"<span>"+arrayn[i].name+"</span></div><div>"; if(i == arrayn.length-1) //最後一個節點 { signalArray.push("blank"); } else signalArray.push("I"); this.generateTreeHtml(arrayn[i].array,signalArray); signalArray.pop(); this.html += "</div>"; } else //沒有子節點 this.html += "<div id='level"+arrayn[i].level+"_"+arrayn[i].id+"'>"+outerbiaoqian+innerbiaoqian+"<span>"+arrayn[i].name+"</span></div>"; } } } //調用此方法建立樹形結構,在這個方法裡依次調用上面的方法完成樹的建立 this.createTree = function () { //this.array = array; this.initLevel(); //add property named level to all the object this.array.sort(this.compare("level")); //sort the array according to the object's property of level this.initAbstructTree(); this.createAbstructTree(array_1); //執行完這個方法後,數組array_1就儲存了樹形結構的資訊,即:array_1是抽象樹的根 this.generateTreeHtml(array_1,this.signalArray); $("#tree").html(this.html); this.success = true; tree_additionalFunction(); } } //建立出樹形結構以後,給樹增加一些事件和樣式 function tree_additionalFunction() { //添加滑鼠經過時的樣式 $("div[id^=level]").hover(function(){ $(this).addClass("dd"); },function(){ $(this).removeClass("dd"); }); //點擊結點時擷取結點的值,然後使樹不可見 $("div[id^=level]").click(function(){ $("#sele").val($(this).find("span").text()); //alert($(this).find("span").text()); $("#tree").slideUp(); }); //展開和收合結點時替換相應的表徵圖 $("div[id^=level]>img").click(function(){ if(this.nodeName == "IMG") { var temp = this.src.split('/'); var index = temp.length-1; if(temp[index] == "Tminus.png" || temp[index] == "Lminus.png") { //左邊的運算式先去掉路徑中最後一個值,即:要被替換的圖片名 temp.pop() == "Tminus.png"?this.src = temp.join('/')+"/Tplus.png":this.src = temp.join('/')+"/Lplus.png"; //替換檔案夾表徵圖 $(this).next().attr("src",temp.join('/')+"/foldericon.png"); $(this).parent().next().slideUp(); return false; } else if(temp[index] == "Tplus.png" || temp[index] == "Lplus.png") { //左邊的運算式先去掉路徑中最後一個值,即:要被替換的圖片名 temp.pop() == "Tplus.png"?this.src = temp.join('/')+"/Tminus.png":this.src = temp.join('/')+"/Lminus.png"; //替換檔案夾表徵圖 $(this).next().attr("src",temp.join('/')+"/openfoldericon.png"); $(this).parent().next().slideDown(); return false; } } }