***************************************
關(guān)鍵字:AJAX,Tree,Struts,DOM
難易度: 中
軟件版本:struts 1.2.8
時(shí)間:2006-01-27
Author:Kino
***************************************
前陣子寫(xiě)了一個(gè)小代碼處理AJAX下WebTree的構(gòu)建,其中碰到了一些問(wèn)題,也有一些想法,現(xiàn)在說(shuō)出來(lái)希望大家一起看看,如果案由問(wèn)題請(qǐng)不吝賜教,本人不勝感激啊。
背景:
這次因?yàn)槭羌虞d在Struts上的開(kāi)發(fā),Web頁(yè)面上的Tree作AJAX處理,因?yàn)橛蠳ode的增刪改操作。Server端因?yàn)橐蚖ebService連接,所以不做Cache。
解決案:
1。使用Polling調(diào)用AJAX定期更新Tree。
2。AJAX訪問(wèn)的地址是一個(gè)Action(例:createtree.do)。用來(lái)返回Tree模型或者錯(cuò)誤消息(國(guó)際化)。
3。Browser解析XML的TreeModel。
4。在Browser比較新舊2個(gè)TreeModel,完成選中狀態(tài)的繼承。
5。CSS渲染TreeNode。
以上是簡(jiǎn)單的思路。傳統(tǒng)的AJAX應(yīng)該是盡量減少XMl傳輸量,迫于沒(méi)有Cache的緣故,并且WS給我的節(jié)點(diǎn)并不能簡(jiǎn)單的得到父子關(guān)系。我選擇了,每次Polling更新整棵樹(shù)的方案。性能未測(cè)。
我這次在web server 端構(gòu)建Tree時(shí)直接用深度優(yōu)先轉(zhuǎn)換成XML。XMl中數(shù)據(jù)的先后順序決定了Tree從父到子,從兄到弟的深度優(yōu)先關(guān)系,indent決定了深度(也就是縮進(jìn))。這樣我從Server端傳入的也就成了一個(gè)標(biāo)準(zhǔn)的Tree顯示Model。格式定義如下。
* Gobal Master Tree DTD
* <!ELEMENT tree (tree*)>
* <!ATTLIST tree
* id CDATA #REQUIRED LoctionInfo's toString
* indent CDATA #REQUIRED Tree's Level
* text CDATA #REQUIRED label in html
* tooltip CDATA #IMPLIED title in html
* action CDATA #IMPLIED href in html
* icon CDATA #IMPLIED close icon with the node status
* openicon CDATA #IMPLIED open icon with the node status
* open CDATA #IMPLIED> node's open states ,default is false in server.
* target CDATA #IMPLIED node's open target
*
<span id="maintree">
<tree id="Ajax" indent=0 text="Root" tooltip="Root" action="/logout.do" icon="" openicon= "" open="false"/>
<tree id="110" indent=1 text="Node 1" tooltip="Node 1" action="/logout.do" icon="" openicon= "" open="false"/>
<tree id="120" indent=2 text="Node 2" tooltip="Node 2" action="/logout.do" icon="" openicon= "" open="false"/>
<tree id="12580" indent=2 text="Node 3" tooltip="Node 3" action="/logout.do" icon="" openicon= "" open="false"/>
<tree id="user" indent=1 text="Node 4" tooltip="Node 4" action="/logout.do" icon="" openicon= "" open="false"/>
</span>
上邊的 Tree顯示出來(lái)如下
Root
│
├Node 1
│ │
│ ├Node 2
│ └Node 3
└Node 4
indent 就是縮進(jìn)。
數(shù)據(jù)的先后順序就是深度優(yōu)先的遍歷順序。
這樣的數(shù)據(jù)到了Browser,會(huì)先被轉(zhuǎn)成一個(gè)對(duì)象數(shù)組。
1
// Tree Node object
2
// This function creates a node in the tree with the following arguments:
3
// sId - The node's index within the global nodes_array
4
// iIndent - The level within the tree hierarchy (0 = top)
5
// sText - The text displayed in the tree for this node
6
// sTooltip - the tool tip
7
// oAction - For a document, the address it will display when clicked
8
// sIcon - the node's icon
9
// sIconOpen - the node's icon state
10
// bOpen - true false
11
function GMTreeNode(sId,iIndent,sText,sTooltip,sAction,sIcon,sIconOpen,bOpen,sTarget)
12

{
13
if (sId) this.id = sId;
14
if (iIndent) this.indent = iIndent;
15
if (sText) this.text = sText;
16
if (sAction) this.action = sAction;
17
if (sTooltip) this.tooltip = sTooltip;
18
if (sIcon) this.icon = sIcon;
19
if (sIconOpen) this.iconopen = sIconOpen;
20
if (bOpen) this.open = bOpen;
21
if (sTarget) this.target = sTarget;
22
23
// //alert(this.id + " " + this.indent + " " + this.text + " " + this.action + " " +
24
// this.tooltip + " " + this.icon + " " + this.iconopen + " " + this.open);
25
} 然后會(huì)和正在顯示的Tree數(shù)組 進(jìn)行一個(gè) 比較,用于寫(xiě)入展開(kāi)狀態(tài),代碼如下:
1
/////////////////////////////////
2
// >>>Compare
3
// compare maintree with maintree. and copy maintree to maintree
4
/////////////////////////////////
5
function compareTreeModel()
6

{
7
//alert("I am here in compareTreeModel");
8
if (ajaxtree.length <= 0)
{
9
alert("ajaxtree is null");//TODO
10
return;
11
}
12
13
if (maintree.length <= 0)
{
14
//alert("maintree is null");
15
maintree = ajaxtree;
16
return;
17
}
18
//compare start
19
//var maxlen = Math.max(ajaxtree.length,maintree.length);
20
for(var i=0;i<ajaxtree.length;i++)
21
{
22
for(var j=0;j<maintree.length;j++)
23
{
24
if (ajaxtree[i].id == maintree[j].id)
25
{
26
ajaxtree[i].open = maintree[j].open;
27
break;
28
}
29
}
30
}
31
32
maintree = ajaxtree;
33
34
} maintree 就是正在顯示的 Tree,ajaxtree是剛得到的新Tree。2個(gè)對(duì)象都是TreeNode的數(shù)組。而TreeNode對(duì)象的Open屬性即記錄了節(jié)點(diǎn)的展開(kāi)狀態(tài)。那么這里就對(duì)這個(gè)節(jié)點(diǎn)狀態(tài)進(jìn)行“移植”,完畢后,把新Tree模型交付顯示方法,也就是ToHtml方法。
轉(zhuǎn)換Html部分算是一個(gè)比較容易出bug的危險(xiǎn)點(diǎn)。
首先分析一下,生成的代碼是什么樣子的。
這里仍然用上邊的那棵樹(shù)作例子。
生成的DOM結(jié)構(gòu)應(yīng)該是
<div 父>
<div 收縮>
<img 折線 />
<img 圖標(biāo) />
<a 節(jié)點(diǎn)動(dòng)作>節(jié)點(diǎn)Label</a>
<div 子>
....遞歸的構(gòu)造
</div 子>
</div 收縮>
</div 父>
其次 對(duì)于這種并不直接含有父子關(guān)系的節(jié)點(diǎn)首先要判明一個(gè)節(jié)點(diǎn)的子 和 兄弟,然后用遞歸解決。
遞歸的思路如下深度優(yōu)先:
function toHtml(節(jié)點(diǎn)index)
{
var child_html;
if 這個(gè)節(jié)點(diǎn)有子
{
Loop子節(jié)點(diǎn)
{
child_html[i] = toHtml(子節(jié)點(diǎn)index);
i++;訪問(wèn)下一個(gè)子節(jié)點(diǎn)
}
var 所有子節(jié)點(diǎn)Html模塊 = <div 收縮> child_html.join("") </div>
var 本節(jié)點(diǎn)Html模塊 = <div 本節(jié)點(diǎn)><div 收縮><img ><img 圖標(biāo) /><a 節(jié)點(diǎn)動(dòng)作>節(jié)點(diǎn)Label</a>所有子節(jié)點(diǎn)Html模塊</div 收縮></div 本節(jié)點(diǎn)>;
return 本節(jié)點(diǎn)Html模塊;
}
樹(shù)就構(gòu)建好了。
作為顯示,使用了CSS的
background-repeat: repeat-y;
background-image: url("../images/tree/I.png") !important;
background-position-y: 1px !important; /* IE only */
還有padding-left作Div的向右偏移,默認(rèn)的偏移量是19個(gè)像素點(diǎn),然后根據(jù)Tree顯示模型的indent相乘就ok了。
思路就是這些。希望能對(duì) 朋友們有所幫助。
歡迎討論。