Posted on 2006-01-27 16:42
-=Kinohl=- 阅读(2695)
评论(7) 编辑 收藏 所属分类:
编程语言
***************************************
关键字:AJAX,Tree,Struts,DOM
难易度: 中
软件版本:struts 1.2.8
时间:2006-01-27
Author:Kino
***************************************
前阵子写了一个小代码处理AJAX下WebTree的构建,其中碰到了一些问题,也有一些想法,现在说出来希望大家一起看看,如果案由问题请不吝赐教,本人不胜感激啊。
背景:
这次因为是加载在Struts上的开发,Web页面上的Tree作AJAX处理,因为有Node的增删改操作。Server端因为要和WebService连接,所以不做Cache。
解决案:
1。使用Polling调用AJAX定期更新Tree。
2。AJAX访问的地址是一个Action(例:createtree.do)。用来返回Tree模型或者错误消息(国际化)。
3。Browser解析XML的TreeModel。
4。在Browser比较新旧2个TreeModel,完成选中状态的继承。
5。CSS渲染TreeNode。
以上是简单的思路。传统的AJAX应该是尽量减少XMl传输量,迫于没有Cache的缘故,并且WS给我的节点并不能简单的得到父子关系。我选择了,每次Polling更新整棵树的方案。性能未测。
我这次在web server 端构建Tree时直接用深度优先转换成XML。XMl中数据的先后顺序决定了Tree从父到子,从兄到弟的深度优先关系,indent决定了深度(也就是缩进)。这样我从Server端传入的也就成了一个标准的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显示出来如下
Root
│
├Node 1
│ │
│ ├Node 2
│ └Node 3
└Node 4
indent 就是缩进。
数据的先后顺序就是深度优先的遍历顺序。
这样的数据到了Browser,会先被转成一个对象数组。
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
11function 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} 然后会和正在显示的Tree数组 进行一个 比较,用于写入展开状态,代码如下:
1/////////////////////////////////
2// >>>Compare
3// compare maintree with maintree. and copy maintree to maintree
4/////////////////////////////////
5function 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个对象都是TreeNode的数组。而TreeNode对象的Open属性即记录了节点的展开状态。那么这里就对这个节点状态进行“移植”,完毕后,把新Tree模型交付显示方法,也就是ToHtml方法。
转换Html部分算是一个比较容易出bug的危险点。
首先分析一下,生成的代码是什么样子的。
这里仍然用上边的那棵树作例子。
生成的DOM结构应该是
<div 父>
<div 收缩>
<img 折线 />
<img 图标 />
<a 节点动作>节点Label</a>
<div 子>
....递归的构造
</div 子>
</div 收缩>
</div 父>
其次 对于这种并不直接含有父子关系的节点首先要判明一个节点的子 和 兄弟,然后用递归解决。
递归的思路如下深度优先:
function toHtml(节点index)
{
var child_html;
if 这个节点有子
{
Loop子节点
{
child_html[i] = toHtml(子节点index);
i++;访问下一个子节点
}
var 所有子节点Html模块 = <div 收缩> child_html.join("") </div>
var 本节点Html模块 = <div 本节点><div 收缩><img ><img 图标 /><a 节点动作>节点Label</a>所有子节点Html模块</div 收缩></div 本节点>;
return 本节点Html模块;
}
树就构建好了。
作为显示,使用了CSS的
background-repeat: repeat-y;
background-image: url("../images/tree/I.png") !important;
background-position-y: 1px !important; /* IE only */
还有padding-left作Div的向右偏移,默认的偏移量是19个像素点,然后根据Tree显示模型的indent相乘就ok了。
思路就是这些。希望能对 朋友们有所帮助。
欢迎讨论。