创建目录树 本文要求读者熟悉JScript,MSXML,XSL/XSLT,DOM。
下载例子代码 简介 XML和XSL/XSLT是当今越来越流行的一种数据处理技术。本文是系列文章的第一篇,介绍如何用XML和XSL/XSLT设计和创建目录树。后续的每一篇文章都将介绍如何用XML和XSL/XSLT设计和创建一个特定的高级Web 应用程序用户界面对象。 本文创建的目录树层次可以是无限制的。其建立机制是通过特定的XSL风格页将定义好的目录树XML文件转换成满足要求的HTML推送给客户端浏览器(IE5.5+)显示。客户端负责处理所有对目录的操作,如展开/收缩、最大化/最小化子目录和整个目录结点。 目录树结构介绍 与建立大多数Web页面对象一样,用XML/XSLT创建目录树的方法有很多,关键是确定目录树的结构,如图一是本文创建的一个目录树例子:
图一 嵌套的目录树结构 目录树中使用线条主要是为了有一个较好的可视化效果。但反过来会影响性能。有100个项的树大概要用300个图像来显示实体间的关系。尽管可以使用缓冲和预先加载,但在DOM中仍然犹如单个对象。不使用线条,而用简单的缩进可以说明父子之间的关系。本文中拟使用不带线条的树结构。 描述目录树结构的XML 文件 目录树的结构根据需要可以是多种多样的。XML为我们提供了无数的可能性来描述树型目录界面的结构。你所选择的格式直接影响XSL风格页的行为和客户的操作。本文所选则的目录树格式比较适用于XSL进行递归处理,它能很好地满足和实现无限深度的目录树的需要。 本文范例构造的XML文档包含一个根元素"tree",这个根元素下只有一个元素"entity"。可以把它看成是目录树的一个节点,整个树的结构是通过在每一层的entity元素的"contents"子元素中嵌套的entity元素来实现的。下面是"entity"元素中包含的关键所有元素或属性的清单。
名称 | 类型 | 描述 |
id | 属性 | 目录节点元素(entity)的id号,唯一标示一个节点 |
description | 元素 | 显示给用户的文本,目录节点元素的描述 |
onClick | 元素 | 客户端触发onClick事件的函数名 |
image | 元素 | 当目录节点被关闭或出于非选中状态时要显示的图像 |
imageOpen | 元素 | 当目录节点打开时显示的图像 |
contents | 元素 | 包含目录节点元素,其内容用以确定目录节点元素是否有子元素 |
在这个树结构中,用entity元素来添加与每一个树节点有关的特定数据。这些特定的数据可以是entity所表示的数据库记录的ID。在后续文章中,我们还将探讨如何创建目录节点的上下文菜单。我们将添加另外一个元素"oncontextmenu"到entity元素中。 下面的XML是本文所使用的目录树结构XML文件。这是一个静态的XML文件,在实际使用中,它可能是一个从数据库记录查询中产生出来的XML文件。
<?xml version="1.0" encoding="gb2312"?> <tree> <entity id="e1"> <description>在线杂志</description> <image>images/book.gif</image> <imageOpen>images/bookOpen.gif</imageOpen> <contents> <entity id="e2"> <description>第十二期</description> <image>images/book.gif</image> <imageOpen>images/bookOpen.gif</imageOpen> <onClick></onClick> <contents> <entity id="e3"> <description>组件对象模型</description> <image>images/book.gif</image> <imageOpen>images/bookOpen.gif</imageOpen> <onClick></onClick> <contents> <entity id="e4"> <description>OLE 编程</description> <image>images/paper.gif</image> <imageOpen>images/paper.gif</imageOpen> <onClick></onClick> <contents/> </entity> <entity id="e5"> <description>ATL编程</description> <image>images/book.gif</image> <imageOpen>images/bookOpen.gif</imageOpen> <onClick></onClick> <contents> <entity id="e51"> <description>OLE 编程</description> <image>images/paper.gif</image> <imageOpen>images/paper.gif</imageOpen> <onClick></onClick> <contents/> </entity> <entity id="e52"> <description>ATL编程</description> <image>images/paper.gif</image> <imageOpen>images/paper.gif</imageOpen> <onClick></onClick> <contents/> </entity> </contents> </entity> </contents> </entity> <entity id="e3"> <description>dotNet框架</description> <image>images/book.gif</image> <imageOpen>images/bookOpen.gif</imageOpen> <onClick></onClick> <contents> <entity id="e4"> <description>C# 编程</description> <image>images/paper.gif</image> <imageOpen>images/paper.gif</imageOpen> <onClick></onClick> <contents/> </entity> <entity id="e5"> <description>数据库编程</description> <image>images/paper.gif</image> <imageOpen>images/paper.gif</imageOpen> <onClick></onClick> <contents/> </entity> </contents> </entity> </contents> </entity> <entity id="e2"> <description>第十三期</description> <image>images/book.gif</image> <imageOpen>images/bookOpen.gif</imageOpen> <onClick></onClick> <contents> <entity id="e3"> <description>组件对象模型</description> <image>images/book.gif</image> <imageOpen>images/bookOpen.gif</imageOpen> <onClick></onClick> <contents> <entity id="e4"> <description>OLE 编程</description> <image>images/paper.gif</image> <imageOpen>images/paper.gif</imageOpen> <onClick>www.vckbase.com/index.html</onClick> <contents/> </entity> <entity id="e5"> <description>ATL编程</description> <image>images/paper.gif</image> <imageOpen>images/paper.gif</imageOpen> <onClick>www.vckbase.com/index.html</onClick> <contents/> </entity> </contents> </entity> <entity id="e3"> <description>dotNet框架</description> <image>images/book.gif</image> <imageOpen>images/bookOpen.gif</imageOpen> <onClick></onClick> <contents> <entity id="e4"> <description>C# 编程</description> <image>images/paper.gif</image> <imageOpen>images/paper.gif</imageOpen> <onClick></onClick> <contents/> </entity> <entity id="e5"> <description>数据库编程</description> <image>images/paper.gif</image> <imageOpen>images/paper.gif</imageOpen> <onClick></onClick> <contents/> </entity> </contents> </entity> </contents> </entity> </contents> </entity> </tree>上面这个XML文件名为tree.xml,包含在可下载的例子代码中。下面我们来讨论如何在客户端浏览器中显示这个目录结构。 XSLT 式样文件 图一就是用式样文件XSLT转换XML的结果,其代码全文如下,它是应用到目录树XML文档的标准XSL风格页:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl" language="JavaScript"> <xsl:template match="tree"> <xsl:apply-templates select="entity"/> </xsl:template> <xsl:template match="entity"> <div οnclick="window.event.cancelBubble = true;clickOnEntity(this);" onselectstart="return false" οndragstart="return false"> <xsl:attribute name="image"><xsl:value-of select="image"/></xsl:attribute> <xsl:attribute name="imageOpen"><xsl:value-of select="imageOpen"/></xsl:attribute> <xsl:attribute name="open">false</xsl:attribute> <xsl:attribute name="id">f<xsl:value-of select="@id"/></xsl:attribute> <xsl:attribute name="open">false</xsl:attribute> <xsl:attribute name="STYLE"> padding-left: 20px; cursor: hand; <xsl:if expr="depth(this) > 2"> display: none; </xsl:if> </xsl:attribute> <table border="0" cellspacing="0" cellpadding="0"> <tr> <td valign="middle"> <img border="0" id="image"> <xsl:attribute name="SRC"> <xsl:value-of select="image"/> </xsl:attribute> </img> </td> <td valign="middle" nowrap="true"> <xsl:attribute name="STYLE"> padding-left: 7px; font-family: Verdana; font-size: 11px; font-color: black; </xsl:attribute> <xsl:value-of select="description"/></td> </tr> </table> <xsl:apply-templates select="contents/entity"/> </div> </xsl:template> </xsl:stylesheet>客户端操作 为了让目录树按照我们自己的意图运行,还需要在客户端做一些工作:例如初始化、实现展开、收缩目录节点等功能……。归纳起来就是下面这些脚本函数: Initialize 初始化 Expand 展开单个节点 Collapse 收缩单个节点 Expand All (Maximize) 展开所有节点 Collapse All (Minimize) 收缩所有节点 下面是以上五个客户端操作的详细脚本代码。它们都放在文件tree.js中。
function initialize() { var xmlDoc var xslDoc xmlDoc = new ActiveXObject(''''Microsoft.XMLDOM'''') xmlDoc.async = false; xslDoc = new ActiveXObject(''''Microsoft.XMLDOM'''') xslDoc.async = false; xmlDoc.load("tree/tree.xml") xslDoc.load("tree/tree.xsl") folderTree.innerHTML = xmlDoc.documentElement.transformNode(xslDoc) } function clickOnEntity(entity) { if(entity.open == "false") { expand(entity, true) } else { collapse(entity) } window.event.cancelBubble = true } function expand(entity) { var oImage oImage = entity.childNodes(0).all["image"] oImage.src = entity.imageOpen for(i=0; i < entity.childNodes.length; i++) { if(entity.childNodes(i).tagName == "DIV") { entity.childNodes(i).style.display = "block" } } entity.open = "true" } function collapse(entity) { var oImage var i oImage = entity.childNodes(0).all["image"] oImage.src = entity.image // 收缩和隐藏字节点 for(i=0; i < entity.childNodes.length; i++) { if(entity.childNodes(i).tagName == "DIV") { if(entity.id != "folderTree") entity.childNodes(i).style.display = "none" collapse(entity.childNodes(i)) } } entity.open = "false" } function expandAll(entity) { var oImage var i expand(entity, false) // 展开子节点 for(i=0; i < entity.childNodes.length; i++) { if(entity.childNodes(i).tagName == "DIV") { expandAll(entity.childNodes(i)) } } }随着Web技术的进步,维护Web应用程序的状态逐渐成为可能。更多的处理和操作将从服务器端移到客户端进行,从而减轻服务器的工作负担,使得我们的程序界面越来越复杂,同时,应用程序的可用性和性能也需要不断的改进。 希望本文能抛砖引玉,共同探讨目前比较时髦的XML/XSL技术,利用它们来提高Web应用程序界面的质量。我们将在下一篇文章中继续我们的主题,讨论如何在目录树中创建上下文菜单。(