如果一次性獲取完整的先序樹,構(gòu)造成xml提供給JavaScript解析,數(shù)據(jù)量越大,消耗的資源越多,客戶端響應(yīng)延遲時間就越長,因此對于大數(shù)據(jù)量的樹,采用動態(tài)加載方式,即每次單擊“+”圖片時,判斷是否已加載子節(jié)點數(shù)據(jù),如果未加載則通過Ajax的XMLHTTP組件XMLHTTPRequest對象異步發(fā)送請求,連接服務(wù)器執(zhí)行SQL 語句“select * from tree_class where parent = ?order by classcode ”獲取節(jié)點數(shù)據(jù)。相關(guān)JavaScript 代碼如下:
dhtmlTree.prototype.Loading=function(pObject){
if(((pObject.XMLload==0)&&(this.XMLsource))&&(!this.XMLloading)){
pObject.XMLload=1;
this.loadXML(this.XMLsource+getUrlSymbol(this.XMLsource)+"id="+escape(pObject.id));
}
}
dtmlXMLObject.prototype.loadXML=function(url){//加載數(shù)據(jù)
try {
this.xmlDoc = new XMLHttpRequest();
/*通過GET方法異步連接到 url 加載數(shù)據(jù)*/
this.xmlDoc.open("GET", url,true);//true:異步;false:同步
this.xmlDoc.send(null);
} catch(e){
this.xmlDoc = new ActiveXObject("Microsoft.XMLHTTP");//使用IE
this.xmlDoc.open("GET", url,true);//true:異步;false:同步
this.xmlDoc.send(null);
}
return this.xmlDoc.responseXML;
}
每次只取同一個父節(jié)點ParentId的子節(jié)點序列,按XML格式封裝成樹的文檔結(jié)構(gòu),例如:
<tree id="0">
<leaf child=”1" name="國防科技大學(xué)" id="1" im0="leaf.gif" im1="folderOpen.gif" im2=" folderClosed.gif"/>
</tree>
提供給JavaScript的dhtmlTreeObject.prototype.insertItem()解析并組織好html輸出節(jié)點;其中child:1表示有子節(jié)點,0表示沒有子節(jié)點;im0表示沒有子節(jié)點時的圖標(biāo);im1表示有子節(jié)點并且打開節(jié)點時的圖標(biāo);im2表示有子節(jié)點并且關(guān)閉時的圖標(biāo);所以還可以在構(gòu)造XML時自定義圖標(biāo)。
2.3.2 樹型結(jié)構(gòu)的構(gòu)造
從數(shù)據(jù)庫中返回的是有序的先序樹,而XML是完整的樹型結(jié)構(gòu)文檔,所以將樹型數(shù)據(jù)構(gòu)造成預(yù)定義的XML格式,只需從根節(jié)點開始,遍歷一遍樹,即可將樹全部生成。相關(guān)JavaScript代碼如下:
/*動態(tài)加載樹的構(gòu)造方法*/
dtmlXMLObject.prototype.constructTree=function(){
//采用動態(tài)加載時獲取的xml數(shù)據(jù),解析樹型數(shù)據(jù)
var node=this.XMLLoader.getXMLTopNode("tree");
var parentId=node.getAttribute("id");
for(var i=0;i<node.childNodes.length;i++) { //逐個解析xml文件的leaf節(jié)點
if((node.childNodes[i].nodeType==1)&&(node.childNodes[i].tagName == "leaf")){
var name=node.childNodes[i].getAttribute("text");
…………
var temp=dhtmlObject.a0Find(parentId);//獲取父節(jié)點對象
temp.XMLload=1;//已加載
//構(gòu)造html輸出節(jié)點
dhtmlObject.insertItem(parentId,cId,name,im0,im1,im2,chd);
dhtmlObject.addDragger = this;//設(shè)置可拖放的對象
};
}
2.3.3 樹型結(jié)構(gòu)的維護(hù)
在維護(hù)樹型結(jié)構(gòu)表時,刪除節(jié)點較為簡單,SQL 語句為: "delete from tree_class where classcode like′"+ classcode +"%′",即可將其節(jié)點和孩子一并刪除;增加節(jié)點時,分為前插、后插、和插入子節(jié)點三種情況,前兩種情況需要更新遞歸更新類別代碼,后者只需找到父節(jié)點的孩子的最大類別代碼加1 后,作為增加節(jié)點的類別代碼;通過拖放來改變樹的結(jié)構(gòu)時,只需將拖動節(jié)點的parentId更新為目標(biāo)節(jié)點的Classid即可,對應(yīng)的SQL語句為:"update tree_class set parentId = "+ classidTo+" where classid = "+ classidFrom。
3、效率分析
對于樹的存儲一般有兩種形式:二維表和鏈表,遍歷方式一般也有深度遍歷和廣度遍歷兩種方式,遍歷的時間復(fù)雜度都是O( n )。用二維表存儲時,在內(nèi)存中用數(shù)組的下標(biāo)能準(zhǔn)確定位節(jié)點的父節(jié)點、兄弟節(jié)點所在的數(shù)組下標(biāo)。數(shù)據(jù)庫中節(jié)點的定位也是準(zhǔn)確的,但是將節(jié)點信息從數(shù)據(jù)庫中讀到內(nèi)存中時,如果無法通過內(nèi)存數(shù)組下標(biāo)定位節(jié)點信息,那么就必須遍歷一遍尋找一個節(jié)點,n 個節(jié)點中尋找一個節(jié)點的時間是O(n/2),n 個節(jié)點排序的時間復(fù)雜度將是O( n2/2),這也是一般實現(xiàn)的B/S 模式的樹結(jié)構(gòu)效率低下的原因。本方案采用字典序編號方案,使得從數(shù)據(jù)庫中取得的樹是已經(jīng)排序的,直接遍歷生成客戶頁面程序,時間復(fù)雜度為O( n )。
4、結(jié) 論
本文討論了基于Ajax的動態(tài)樹型結(jié)構(gòu)的實現(xiàn)方案,支持無刷新動態(tài)維護(hù)樹的節(jié)點信息,支持拖放節(jié)點改變樹的節(jié)點結(jié)構(gòu)以及次序;同時采用數(shù)據(jù)庫存儲節(jié)點信息,保證了該方案有一定的通用性,此外結(jié)合XML描述樹的節(jié)點信息,使得任何按本方案預(yù)定的xml文檔描述的信息都可以通過樹來展現(xiàn)。本方案已經(jīng)應(yīng)用在我校的數(shù)字迎新系統(tǒng)以及老百姓大藥房信息系統(tǒng)中。