基於AJAX的動態樹型結構的設計與實現

 樹型結構是一類應用很是普遍的數據結構。人類社會中宗族的族譜和現代企業的組織形式都是樹型結構。在計算機領域中,文件系統中文件的管理結構、存儲器管理中的頁表、數據庫中的索引等也都是樹型結構。隨着Internet的飛速發展,樹型結構在瀏覽器/服務器(Browser/Server,簡稱B/S)應用系統的應用也愈來愈普遍。

  目前,在互聯網上普遍存在、應用的樹型結構通常分爲兩種:靜態和動態結構。靜態結構存在最多、實現簡單,可是靜態致使不能改變樹的結構和內容,沒法反映樹的節點信息的變化;而實現相對複雜的動態構造樹,雖然能夠動態增長、刪除、更新節點信息,可是大部分不能直接拖放節點來改變樹的結構以及節點間的次序,而且反覆刷新整個頁面,給用戶維護帶來了許多不便。本文提出了一種基於Ajax(Asynchronous JavaScript and XML)通用的、動態加載節點的解決方案。實現上採用J2EE多層架構,樹節點的描述信息採用數據庫存儲,以可擴展標記語言(eXtensible Markup Language,簡稱XML)展示給JavaScript解析,支持無刷新地增長、刪除、更新節點信息,以及拖放節點來改變樹的結構和節點間的次序。文中第1部分簡要介紹了Ajax技術;第2部分詳細介紹了該方案的技術實現過程;第3部分分析了該方案的效率。 bitsCN_com

  一、Ajax簡介

  Ajax概念的最先提出者Jesse James Garrett認爲:Ajax並非一門新的語言或技術,它其實是幾項技術按必定的方式組合在共同的協做中發揮各自的做用,它包括:

  ?使用擴展超媒體標記語言(eXtended Hypertext Markup Language,簡稱XHTML)和級聯樣式單(Cascading Style Sheet,簡稱CSS)標準化呈現;

  ?使用文檔對象模型(Document Object Model,簡稱DOM)實現動態顯示和交互;

  ?使用可擴展標記語言(eXtensible Markup Language,簡稱XML)和可擴展樣式錶轉換(eXtensible Stylesheet Language Transformation,簡稱XSLT)進行數據交換與處理;

  ?使用XMLHTTP組件XMLHttpRequest對象進行異步數據讀取;

  ?最後用JavaScript綁定和處理全部數據。

  Ajax的工做原理如圖1所示,它至關於在用戶和服務器之間加了一箇中間層,使用戶操做與服務器響應異步化。並非全部的用戶請求都提交給服務器,像?些數據驗證和數據處理等都交給Ajax引擎處理,只有肯定須要從服務器讀取新數據時再由Ajax引擎代爲向服務器提交請求。這樣就把一些服務器負擔的工做轉嫁到客戶端,利用客戶端閒置的處理能力來處理,減輕服務器和帶寬的負擔,從而達到節約ISP的空間及帶寬租用成本的目的。 須要什麼來搜一搜吧so.bitsCN.com


圖 1 未使用Ajax(a)和使用Ajax(b)的web應用比較

  二、整體設計方案

  傳統的服務器程序採用Model 1開發模型,一般將業務邏輯、服務器端處理過程和HTML代碼集中在一塊兒表示,快速完成應用開發。Model 1 在小規模應用開發時優點明顯,可是應用實現通常是基於過程的,一組服務器頁面實現一個流程,若是流程改動將致使多個地方修改,很是不利於應用的擴展和更新。此外業務邏輯和表示邏輯混合在服務器頁面中,耦合緊密,沒法模塊化,致使代碼沒法複用。

  Model 2則解決了這些問題,它是面向對象的MVC模式(Model-View-Controller,模型-視圖-控制器)在Web開發中的應用,Model表示應用的業務邏輯,View是應用的表示層頁面,Controller是提供應用的處理過程控制。經過這種MVC設計模式把應用邏輯,處理過程和顯示邏輯劃分紅不一樣的組件、模塊實現,組件間能夠進行交互和重用。

  本方案是採用J2EE的多層架構,設計時結合Struts框架將表示層、業務邏輯層和數據層劃分紅不一樣的模塊。表示層專一於樹的外觀顯示,業務邏輯層爲服務器端處理程序,處理樹的生成、變化,爲減小耦合性,該程序所有模塊化實現,不在表示頁面嵌入服務器程序;模型層是數據的存儲和表示。下面分別介紹各層實現。 bbs.bitsCN.com國內最先的網管論壇

  2.1 表示層實現

  相似Windows資源管理器的文件夾模式,節點的圖片樣式如表1所示。對於每一個節點的DHTML 代碼,須要包含節點的位置、前導圖片、樣式、針對該節點的其餘操做等。同時爲了節點顯示的連貫性,還需一些前導圖片。

  表1 樹節點的前的圖片樣式表


  對於樹的非葉子節點,圖片和節點信息等,採用一個DIV ( division) 容器包含。DIV 等容器是DHTML 的基礎,使用它能夠經過腳本程序對其屬性進行操做,如設置其style 樣式的display 屬性來控制子節點的展開和隱藏。節點的位置、前導圖片、樣式、針對該節點的其餘的操做等都放入容器中,例:
< DIV id =mParentID>
< IMG align = center border = 0 onclick =″nodeExpand (‘leafid’)″ name = m1Tree src =′Tplus.gif′>
< IMG align = center border = 0 name = m1Folder src =′folderClosed. gif′> 計算機學院 </DIV>

  葉子節點無需容器直接輸出便可。

  當點擊某節點前的「 + 」、「 - 」圖片時經過DIV 的style 樣式的display 屬性控制子節點的展開和隱藏。display:「none」(隱藏,不可見),display:「block」(顯示) 。相關JavaScript 代碼以下: feedom.net國內最先的網管網站
if (expandChild.style.display = =″none″){
 // 當前爲隱藏狀態,執行展開動做
 this.Loading(parentObject);//判斷該分支的數據是否已經加載
 expandChild.style.display =″block″;
if (para2 = =″last″)
 parentObject.src =″Lminus. gif″; // 最後一個節點
else
 parentObject.src = ″Tminus. gif″; // 顯示┠
 expandFolder.src = ″folderOpen. gif″;
}else {
 // 將當前節點的子節點所有隱藏
 expandChild.style.display = ″none″;
 if (para2 = = ″last″)
  parentObject.src = ″Lplus. gif″;
 else
  parentObject.src = ″Tplus. gif″;
  expandFolder.src = ″folderClosed. gif″;
}

  2.2 樹型表結構設計

  咱們以數據庫爲載體記錄節點的變化,樹型表結構至少要有如下字段:節點的編號(CLASSID) ,對節點的描述(ClassName),父節點的編號(ParentId),這些是構建樹結構所必須的信息。同時引入節點的類別代碼(ClassCode),節點的級別(ClassLevel),是否葉子節點 (Terminated)等輔助字段,記錄節點次序,實體關係圖如圖3所示。

dl.bitsCN.com網管軟件下載
圖 3 樹型表結構示意圖


  樹遍歷的時間複雜度是O( n ),可是將樹信息存放到數據庫後,就不能按傳統的方式遍歷樹,必須使用SQL 語句訪問數據庫表的內容,而一次性取的數據量越多,消耗的資源也越多,用戶等待的時間就越長。若是將無序的數據從數據庫中讀出,在服務器端,必須將排序後的樹送到客戶端顯示。所以,最好從數據庫讀出已排好序的樹。

  咱們知道,字符串排序是按照字典序形式。結合SQL 語句的特色和樹結構特色,數據庫表中,節點的類別代碼採用多級字符串形式,如AAABBBCCC,從樹根節點開始,每向下一級字符串就增長一級,而且子節點類別代碼以父節點類別代碼開始,再開始本級的類別代碼。同級的節點按照生成的順序編號,如節點類別代碼爲AAA 的下一級孩子類別代碼爲AAAAAA,AAAAAB 等,AAAAAB 的孩子節點爲AAAAABAAA、AAAAABAAB等。每一級編號字符的寬度與實際的應用關聯,如AAA~ZZZ 一級則有263 個節點,若是不夠用再增長一個字符用於編碼。該巧妙的編號方式。使得在執行SQL 語句select * from tree_class order by classcode 後,一次得到完整的先序樹。

  2.3 業務邏輯層設計

  2.3.1 動態加載技術 play.bitsCN.com累了嗎玩一下吧

  若是一次性獲取完整的先序樹,構形成xml提供給JavaScript解析,數據量越大,消耗的資源越多,客戶端響應延遲時間就越長,所以對於大數據量的樹,採用動態加載方式,即每次單擊「+」圖片時,判斷是否已加載子節點數據,若是未加載則經過Ajax的XMLHTTP組件XMLHTTPRequest對象異步發送請求,鏈接服務器執行SQL 語句「select * from tree_class where parent = ?order by classcode 」獲取節點數據。相關JavaScript 代碼以下:
/*判斷是否已經加載數據,未加載則訪問服務器加載數據*/ html

dl.bitsCN.com網管軟件下載

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){//加載數據
 try {
  this.xmlDoc = new XMLHttpRequest();
  /*經過GET方法異步鏈接到 url 加載數據*/
  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;
}

  每次只取同一個父節點ParentId的子節點序列,按XML格式封裝成樹的文檔結構,例如:
<tree id="0">
<leaf child=」1" name="國防科技大學" id="1" im0="leaf.gif" im1="folderOpen.gif" im2=" folderClosed.gif"/>
</tree>
須要什麼來搜一搜吧so.bitsCN.com node

  提供給JavaScript的dhtmlTreeObject.prototype.insertItem()解析並組織好html輸出節點;其中child:1表示有子節點,0表示沒有子節點;im0表示沒有子節點時的圖標;im1表示有子節點而且打開節點時的圖標;im2表示有子節點而且關閉時的圖標;因此還能夠在構造XML時自定義圖標。

  2.3.2 樹型結構的構造

  從數據庫中返回的是有序的先序樹,而XML是完整的樹型結構文檔,因此將樹型數據構形成預約義的XML格式,只需從根節點開始,遍歷一遍樹,便可將樹所有生成。相關JavaScript代碼以下:
/*動態加載樹的構造方法*/ web

bbs.bitsCN.com國內最先的網管論壇

dtmlXMLObject.prototype.constructTree=function(){ 數據庫

dl.bitsCN.com網管軟件下載

//採用動態加載時獲取的xml數據,解析樹型數據 bitsCN_com 設計模式

var node=this.XMLLoader.getXMLTopNode("tree"); bitsCN_com關注網管是咱們的使命 數組

var parentId=node.getAttribute("id"); bbs.bitsCN.com國內最先的網管論壇 瀏覽器

for(var i=0;i<node.childNodes.length;i++) { //逐個解析xml文件的leaf節點 服務器

bitsCN_com

 if((node.childNodes[i].nodeType==1)&&(node.childNodes[i].tagName == "leaf")){
  var name=node.childNodes[i].getAttribute("text");
  …………
  var temp=dhtmlObject.a0Find(parentId);//獲取父節點對象
  temp.XMLload=1;//已加載
  //構造html輸出節點
  dhtmlObject.insertItem(parentId,cId,name,im0,im1,im2,chd);
  dhtmlObject.addDragger = this;//設置可拖放的對象
 };
}

  2.3.3 樹型結構的維護

  在維護樹型結構表時,刪除節點較爲簡單,SQL 語句爲: "delete from tree_class where classcode like′"+ classcode +"%′",便可將其節點和孩子一併刪除;增長節點時,分爲前插、後插、和插入子節點三種狀況,前兩種狀況須要更新遞歸更新類別代碼,後者只需找到父節點的孩子的最大類別代碼加1 後,做爲增長節點的類別代碼;經過拖放來改變樹的結構時,只需將拖動節點的parentId更新爲目標節點的Classid便可,對應的SQL語句爲:"update tree_class set parentId = "+ classidTo+" where classid = "+ classidFrom。

  三、效率分析

  對於樹的存儲通常有兩種形式:二維表和鏈表,遍歷方式通常也有深度遍歷和廣度遍歷兩種方式,遍歷的時間複雜度都是O( n )。用二維表存儲時,在內存中用數組的下標能準肯定位節點的父節點、兄弟節點所在的數組下標。數據庫中節點的定位也是準確的,可是將節點信息從數據庫中讀到內存中時,若是沒法經過內存數組下標定位節點信息,那麼就必須遍歷一遍尋找一個節點,n 個節點中尋找一個節點的時間是O(n/2),n 個節點排序的時間複雜度將是O( n 2/2),這也是通常實現的B/S 模式的樹結構效率低下的緣由。本方案採用字典序編號方案,使得從數據庫中取得的樹是已經排序的,直接遍歷生成客戶頁面程序,時間複雜度爲O( n )。 數據結構

bitsCN.com中國網管聯盟架構



  四、結 論   本文討論了基於Ajax的動態樹型結構的實現方案,支持無刷新動態維護樹的節點信息,支持拖放節點改變樹的節點結構以及次序;同時採用數據庫存儲節點信息,保證了該方案有必定的通用性,此外結合XML描述樹的節點信息,使得任何按本方案預約的xml文檔描述的信息均可以經過樹來展示。本方案已經應用在我校的數字迎新系統以及老百姓大藥房信息系統中。
相關文章
相關標籤/搜索