今天,我们说说使用Deeptree的一个心得技巧。可能还有不知道Deeptree是什么的兄弟,deeptree是微软最早使用在msdn里作为导航树的,在ajax大行其道之前已经以优秀的设计及出众的性能(不是一次性加载数据,而是使用的时候再加载,套句术语就是属于“lazy loading”)为广大开发者争相采用。
下面,简单介绍下deeptree的组成,它主要包括以下几个文件:
deeptree.htc:负责处理数据加载,用户事件
deeptree.xsl:负责将xml数据格式化为html,展现给用户
deeptree.css:负责树的显示风格
server.asp:负责从服务器获取数据
Deeptree一般是使用单表作为数据源的,表结构一般是类似这样:
列名 | 列类型 |
栏目ID | 自增数字型 |
栏目名称 | 字符型 |
父栏目ID | 数字型 |
deeptree在生成树的过程中,一般流程如下:
- htc里定义了server.asp和deeptree.xsl文件
var Config = {
loading: " 正在加载,请稍候... " , // 加载时显示信息
unavaible: " 加载失败,请检查... " , // 加载失败时显示信息
Service: " getAreaByType.asp " , // 节点数据文件
SyncXSLsrc: " deeptree_area.xsl " , // 一次加载时xsl文件
isExpandOne: true // 同一级别是否只允许打开一个节点
} - htc里调用Init()方法初始化数据
function Init(){
GetXml(element, 0 )
} - GetXml(objContainer,id)方法从服务器获取数据(XMLHTTP),格式如下:
<? xml version="1.0" encoding="gb2312" ?>
< xml >
< TreeNode id ="1" >
< NodeText > 旅游 </ NodeText >
< title ></ title >
< NodeUrl > searchform.php?fdIcoId=1 </ NodeUrl >
< child > 2 </ child >
< target > showresult </ target >
</ TreeNode >
< TreeNode id ="4" >
< NodeText > 房产 </ NodeText >
< title ></ title >
< NodeUrl > searchform.php?fdIcoId=4 </ NodeUrl >
< child > 5 </ child >
< target > showresult </ target >
</ TreeNode >
</ xml > - xml数据+deeptree.xsl文件处理,生成html并显示
- 当用户点击节点的时候,调用htc里的ExpandNode(objNode)方法加载数据(GetXml)
下面,是一个deeptree处理多表的问题,这里是一个地区数据,分为国、省、市三级,这三级的数据分别存在三张表里,如下:
列名 | 列类型 |
国家ID | 自增数字型 |
国家名称 | 字符型 |
列名 | 列类型 |
省份ID | 自增数字型 |
省份名称 | 字符型 |
国家ID | 数字型,外键 |
列名 | 列类型 |
地/市ID | 自增数字型 |
地/市名称 | 字符型 |
省份ID | 数字型,外键 |
问题来了,如何在用户点击节点的时候,判断用户当前点击的是国家、省份还是地市呢?
我的处理方法是这样的:
- 修改GetXml方法,添加一个areaType参数:
function GetXml(objContainer,id,areaType){
var XmlHttp = new ActiveXObject( " Microsoft.XMLHTTP " )
objContainer.innerHTML = StateXML(Config.loading)
objContainer.send = " true "
XmlHttp.onreadystatechange = function (){
if (XmlHttp.readyState == 4 ){
if (XmlHttp.status == 200 ){
var Xmldoc = XmlHttp.responseXML
if (Xmldoc.documentElement.hasChildNodes())
objContainer.innerHTML = Xmldoc.transformNode(xsldoc)
else
objContainer.innerHTML = StateXML(Config.loading)
}
else
objContainer.innerHTML = StateXML(Config.unavaible)
}
}
var url = Config.Service + ' ?id= ' + id + ' &areaType= ' + areaType + ' &tem ' + Math.random()
// alert(url)
XmlHttp.open( " get " ,url, true )
XmlHttp.send()
} - 初始化的时候,areaType默认设为country
function Init(){
GetXml(element, 0 , " country " )
} - 修改ExpandNode方法
function ExpandNode(objNode){
var oImg = GetElement(objNode, " img " )
var oNode = GetElement(objNode, " span " )
var oChild = objNode.nextSibling
oImg.src = icon.open.src
oChild.style.display = ""
objNode.open = " true "
// alert(objNode.areaType)
if (oChild.send == " false " )GetXml(oChild,objNode.uid,objNode.areaType)
if (Config.isExpandOne){
var oContainer = objNode.parentElement
var oChildren = oContainer.children
for (i = 0 ;i < oChildren.length;i ++ )
if (oChildren[i].open == " true " && oChildren[i] != objNode)CollapseNode(oChildren[i])
}
} - 修改XML数据格式,添加areaType属性:
<? xml version="1.0" encoding="gb2312" ?>
< xml >
< TreeNode id ="1" areaType ="province" >
< NodeText > 安哥拉 </ NodeText >
< title ></ title >
< NodeUrl > DD_Area_Form.asp?id=1 & title=安哥拉 & type=country </ NodeUrl >
< child > 0 </ child >
< target > mainFrame </ target >
</ TreeNode >
</ xml > - 修改deeptree.xsl文件,为生成的树节点(div)增加areaType属性,使用xpath从xml数据中获取areaType的值:
< xsl:attribute name ="areaType" >< xsl:value-of select ="@areaType" /></ xsl:attribute > - 修改数据读取程序,其中有个问题,如何判断用户当前点击的是国家、省份还是地市?我们从代码的角度进行分析下,从第二步可以看到,初始化的时候:id=0,areaType=country,系统初始化以后会首先取得国家表的数据,那么当我们点击节点,希望取得的是某个国家内的省份数据,处理方式为:
- 判断id是否为0,若为0则设置xml数据的areaType属性值为province(省)
- 点击节点的时候激活ExpandNode方法,通过GetXml访问如下Xml:
http:/getAreaByType.asp?id=37&areaType=province
取得如下xml数据:
<? xml version="1.0" encoding="gb2312" ?>
< xml >
< TreeNode id ="1" areaType ="city" >
< NodeText > 北京市 </ NodeText >
< title ></ title >
< NodeUrl > DD_Area_Form.asp?id=1 & title=北京市 & type=province </ NodeUrl >
< child > 1 </ child >
< target > mainFrame </ target >
</ TreeNode >< TreeNode id ="2" areaType ="city" >
< NodeText > 天津市 </ NodeText >
< title ></ title >
< NodeUrl > DD_Area_Form.asp?id=2 & title=天津市 & type=province </ NodeUrl >
< child > 1 </ child >
< target > mainFrame </ target >
</ TreeNode >
</ xml > - 此时就会显示某国家的省份数据,此时id不等于0,那么我们可指定areaType=city,那么当用户单击省份的时候就会获取城市的数据:
<? xml version="1.0" encoding="gb2312" ?>
< xml >
< TreeNode id ="134" areaType ="city" >
< NodeText > 南昌市 </ NodeText >
< title ></ title >
< NodeUrl > DD_Area_Form.asp?id=134 & title=南昌市 & type=city </ NodeUrl >
< child > 0 </ child >
< target > mainFrame </ target >
</ TreeNode >< TreeNode id ="135" areaType ="city" >
< NodeText > 景德镇市 </ NodeText >
< title ></ title >
< NodeUrl > DD_Area_Form.asp?id=135 & title=景德镇市 & type=city </ NodeUrl >
< child > 0 </ child >
< target > mainFrame </ target >
</ TreeNode >
</ xml >
- 总结下,这里的问题关键是以下几个方面:
- 为xml数据增加areaType属性;
- 修改deeptree.xsl文件,添加areaType属性;
- 修改deeptree.htc文件,添加areaType参数;
- 根据id来判断地区类型。
- 附:getAreaType.asp源代码:
< %@LANGUAGE = " VBSCRIPT " CODEPAGE = " 936 " % >
< ! -- #include file = " ../System/Conn.asp " -->
< %
' 项目类型
dim id,areaType,target
id = request.QueryString( " id " )
areaType = request.QueryString( " areaType " )
target = " mainFrame "
if areaType = "" then
areaType = " country "
end if
' 项目类型数据集
dim rs
' 查询项目类型sql字符串
dim sql,areaId,areaName
if areaType = " province " then
sql = " select province_id,province_name from d_Province where country_id= " & id & " order by province_id "
areaId = " province_id "
areaName = " province_name "
elseif areaType = " city " then
sql = " select city_id,city_name from d_City where province_id= " & id & " order by city_id "
areaId = " city_id "
areaName = " city_name "
elseif areaType = " country " then
sql = " select Country_id,Country_Chinese_Name from D_Country order by Country_id "
areaId = " Country_id "
areaName = " Country_Chinese_Name "
end if
' 打开数据集
set rs = Conn.execute(sql)
' 定义xml字符串变量
dim xml
xml = " <?xml version=""1.0"" encoding=""gb2312""?> " & vbcrlf
xml = xml & " <xml> " & vbcrlf
' 循环xml文件数据体
while ( not rs.eof)
xml = xml & " <TreeNode id="" " & rs(areaId) & " "" areaType="" " & getAreaType(id) & " ""> " & vbcrlf
xml = xml & " <NodeText> " & rs(areaName) & " </NodeText> " & vbcrlf
xml = xml & " <title></title> " & vbcrlf
xml = xml & " <NodeUrl> " & getUrl(rs(areaId),areaType,rs(areaName)) & " </NodeUrl> " & vbcrlf
xml = xml & " <child> " & getChildNum(rs(areaId),areaType) & " </child> " & vbcrlf
xml = xml & " <target> " & target & " </target> " & vbcrlf
xml = xml & " </TreeNode> "
rs.movenext()
wend
xml = xml & " </xml> " & vbcrlf
' 关掉连接释放资源
rs.close()
conn.close()
set rs = nothing
set conn = nothing
function getUrl(areaId,areaType,areaName)
dim url
if areaType = " country " then
url = " DD_Area_Form.asp?id= " & areaId & " &title= " & areaName & " &type=country "
elseif areaType = " province " then
url = " DD_Area_Form.asp?id= " & areaId & " &title= " & areaName & " &type=province "
elseif areaType = " city " then
url = " DD_Area_Form.asp?id= " & areaId & " &title= " & areaName & " &type=city "
end if
getUrl = url
end function
function getChildNum(areaId,areaType)
dim iCount
if areaType = " country " then
sql2 = " select count(1) as iCount from D_Province where Country_Id= " & areaId
elseif areaType = " province " then
sql2 = " select count(1) as iCount from D_City where Province_Id= " & areaId
elseif areaType = " city " then
getChildNum = " 0 "
exit function
end if
set rsCount1 = conn.execute(sql2)
iCount = rsCount1( " iCount " )
rsCount1.close
set rsCount1 = nothing
getChildNum = iCount
end function
function getAreaType(areaId)
if areaId = " 0 " then
getAreaType = " province "
elseif areaId <> "" and areaId <> " 0 " then
getAreaType = " city "
end if
end function
' 输出xml文件
response.ContentType = " text/xml "
response.Charset = " gb2312 "
response.Write(xml)
% >