用XSLT轻松实现树形折叠导航栏

一般我们见到的XML文件是以元素为结点的,随着层次的不断加深,逐渐成了一棵树,这种文件的好处是我们一看就很明白其中的子、父、祖宗、兄弟关系,不方便之处在于,我个人认为,如果层次很深又有很多的兄弟结点的话,那么文件可能很大而影响处理的效率。正由于XML对描述数据结构的灵活性,所以在某些环境下采用属性值来描述元素之间的关系。例如花园左边的TOC(TABLE OF CONTENT),实现它的XML文件通过属性值来说明元素的类型(NODE OR LEAF), 不过里面仍有子结点存在, 所以用来TRANSFORM它的XSL文件很复杂,分了好几种情况。当然今天我们不是谈花园TOC的实现方法而是用一种更快速、更巧妙的方法来实现类似的TOC,当然也可以叫"树形折叠导航栏"。 好了,废话少说,进入正题。先来看一个很简单的DTD。 NAVIGATOR.DTD
<!ELEMENT Navigation (Navigator*)>
<!ELEMENT Navigator EMPTY>
<!ATTLIST Navigator ID CDATA #IMPLIED AncestorID CDATA #IMPLIED Layer CDATA #IMPLIED Title CDATA #IMPLIED Childs CDATA #IMPLIED Url CDATA #IMPLIED Image CDATA #IMPLIED>

文件很简单,可以这样理解,顶层元素Navigation包含了多个Navigator元素定义了,Navigator不包含元素但有一系列属性。 也许您已经发现, 属性中有两个叫AncestorID Childs的,对了,这两个属性是关键, 当然还有Layer, 在他们的共同作用下, Navigator元素之间的关系将被明确描述。 好了, 我们来看Navi.xml文件, 以花园TOC做为例子。

查看花园TOC例子:

NAVI.xml
<?xml version="1.0" encoding="gb2312"?>
<!DOCTYPE Navi SYSTEM "navigator.dtd">
<?xml-stylesheet type="text/xsl" href="navigator.xsl" ?>
<Navigation>
<Navigator ID='1' AncestorID='1' Layer='0' Title='花园首页' Childs='0' Url='default.asp' Image='images/dc.gif'/>
<Navigator ID='2' AncestorID='2' Layer='0' Title='我的花园' Childs='4' Url='#' Image='default'/>
<Navigator ID='3' AncestorID='2' Layer='1' Title='收藏夹' Childs='4' Url='#' Image='default'/>
<Navigator ID='21' AncestorID='3' Layer='2' Title='我管理的花坛' Childs='0' Url='mybbs.asp?cat=g' Image='images/dc-new.gif'/>
<Navigator ID='22' AncestorID='3' Layer='2' Title='我种下的种子' Childs='0' Url='mybbs.asp?cat=t' Image='images/dc-new.gif'/>
<Navigator ID='23' AncestorID='3' Layer='2' Title='我喜欢的花园' Childs='0' Url='myfavorite.asp?cat=g' Image='images/dc-new.gif'/>
<Navigator ID='24' AncestorID='3' Layer='2' Title='我收藏的文章' Childs='0' Url='myfavorite.asp?cat=t' Image='images/dc-new.gif'/>
<Navigator ID='4' AncestorID='2' Layer='1' Title='个人工具箱' Childs='2' Url='#' Image='default'/>
<Navigator ID='25' AncestorID='4' Layer='2' Title='配置和管理' Childs='0' Url='personal.asp' Image='images/dc-config.gif'/>
<Navigator ID='26' AncestorID='4' Layer='2' Title='花瓣兑换点' Childs='0' Url='apetal.asp' Image='images/dc-config.gif'/>
<Navigator ID='27' AncestorID='2' Layer='1' Title='我的日记本' Childs='0' Url='mydiary.asp' Image='images/dc-diary.gif'/>
<Navigator ID='6' AncestorID='2' Layer='1' Title='好友和短讯' Childs='0' Url='myfriend.asp' Image='images/dc-friends.gif'/>
<Navigator ID='7' AncestorID='7' Layer='0' Title='计算机技术' Childs='2' Url='#' Image='default'/>
<Navigator ID='8' AncestorID='7' Layer='1' Title='DHTML,JScript' Childs='0' Url='bbsgroup.asp?c=6&g=16' Image='images/dc.gif'/>
<Navigator ID='9' AncestorID='7' Layer='1' Title='.NET,ASP+探讨' Childs='0' Url='bbsgroup.asp?c=6&g=17' Image='images/dc.gif'/>
<Navigator ID='10' AncestorID='7' Layer='1' Title='ASP互助' Childs='0' Url='bbsgroup.asp?c=6&g=18' Image='images/dc.gif'/>
<Navigator ID='11' AncestorID='11' Layer='0' Title='箐箐校园' Childs='2' Url='#' Image='default'/>
<Navigator ID='12' AncestorID='11' Layer='1' Title='南京大学' Childs='0' Url='bbsgroup.asp?c=7&g=19' Image='images/dc.gif'/>
<Navigator ID='13' AncestorID='11' Layer='1' Title='东南大学' Childs='0' Url='bbsgroup.asp?c=7&g=20' Image='images/dc.gif'/>
<Navigator ID='14' AncestorID='14' Layer='0' Title='花园·有个广场' Childs='2' Url='#' Image='default'/>
<Navigator ID='15' AncestorID='14' Layer='1' Title='意见箱' Childs='0' Url='bbsgroup.asp?c=8&g=21' Image='images/dc.gif'/>
<Navigator ID='16' AncestorID='14' Layer='1' Title='花园·人物故事' Childs='0' Url='bbsgroup.asp?c=8&g=22' Image='images/dc.gif'/>
<Navigator ID='17' AncestorID='17' Layer='0' Title='园丁办公室' Childs='0' Url='bbsgroup.asp?c=9&g=23' Image='images/dc-key.gif'/>
<Navigator ID='18' AncestorID='18' Layer='0' Title='青青芳草地' Childs='0' Url='bbsgroup.asp?c=9&g=24' Image='images/dc.gif'/>
<Navigator ID='19' AncestorID='19' Layer='0' Title='统计信息' Childs='0' Url='viewlog.asp' Image='images/dc-chart.gif'/>
<Navigator ID='20' AncestorID='20' Layer='0' Title='ActiveCard' Childs='0' Url='activecard?fromgarden' Image='images/dc-card.gif'/>
</Navigation>

结合上面我讲的和花园左边的TOC, 仔细分析这个文件后, 找出元素间存在的关系是很容易的, 难的是怎么想到这么来创建XML文件的。 好了, 有了数据, 下一步就是如何MANUPILATE了。


我引用花园的TOC,一是让大家能有个初步印象,等文章完成后, 把几个文件C&P加上几个图片, 在IE5以上的机器上象打开一个html文件一样打开navi.xml后,就会出现跟花园很类似的TOC了;二是希望大家根据它的层次结构来分析我的xml文件, 因为除顶层外, 我的层次安排和花园是一样的。 我来解释一下:Layer相同表示元素处在同一层次即兄弟关系, Childs的值表示该元素是否有子结点, 父子之间用AncestorID和ID联系, 依次类推可以扩充至无限次深。 在xsl文件中根据Layer的值用padding-left属性来实现树形,根据Layer的值用display:none或block来实现折叠。 原理即此, 好,来看看这个关键的Navigator.xsl: <?xml version="1.0" encoding="gb2312" ?>
<HTML>
<HEAD>
<TITLE>XSLT树形导航栏</TITLE>
<LINK rel="stylesheet" type="text/css" href="navigator.css"/>
<SCRIPT src="toggle.js"></SCRIPT>
</HEAD>
<BODY>

<DIV xmlns:xsl="http://www.w3.org/TR/WD-xsl" >
<TABLE>
<TR>
<TD><DIV noWrap="true" STYLE="padding-left:0em;">有座花园分类目录</DIV></TD>
</TR>
<xsl:for-each select="Navigation/Navigator">
<TR>
<xsl:attribute name="TITLE"><xsl:value-of select="@Title" /></xsl:attribute>
<xsl:attribute name="Class">Navigator<xsl:if test="@Layer[.>0]">-Hidden</xsl:if></xsl:attribute>
<xsl:attribute name="ID"><xsl:value-of select="@ID"/></xsl:attribute>
<xsl:attribute name="AncestorID"><xsl:value-of select="@AncestorID"/></xsl:attribute>
<xsl:attribute name="Depth"><xsl:value-of select="@Layer"/></xsl:attribute>
<xsl:if test="@Childs[.>0]">
<xsl:attribute name="Expanded">no</xsl:attribute>
</xsl:if>
<TD STYLE="cursor:hand">
<DIV noWrap="true"><xsl:attribute name="STYLE">padding-left:<xsl:value-of select="@Layer"/>em;</xsl:attribute>
<xsl:choose>
<xsl:when test="@Childs[.>0]"><IMG src="images/bs.gif"></IMG></xsl:when>
<xsl:otherwise><IMG><xsl:attribute name="src"><xsl:value-of select="@Image" /></xsl:attribute></IMG></xsl:otherwise>
</xsl:choose>
<A><xsl:if test="@Childs[.>0]"><xsl:attribute name="onclick">toggle('<xsl:value-of select="@ID" />')</xsl:attribute></xsl:if><xsl:attribute name="href"><xsl:value-of select="@Url" /></xsl:attribute><xsl:value-of select="@Title" /></A></DIV></TD>
</TR>
</xsl:for-each>
</TABLE>
</DIV>
</BODY>
</HTML>


当然, 少了navigator.css是不行的。

navigator.css

BODY
{
font-family:Verdana;
cursor:default;
font-size:9pt;
}

TABLE
{
font-size:110%;
}

A
{
color:"#003366";
text-decoration:none;
}

A:hover
{
text-decoration:underline;
color:#003366;
}

DIV IMG
{
MARGIN-BOTTOM: 0px;
MARGIN-RIGHT: 5px;
MARGIN-TOP: -3px;
VERTICAL-ALIGN: middle
}

DIV A:hover
{
BACKGROUND-COLOR: greenyellow
}
DIV A
{
FONT-WEIGHT: normal;
MARGIN-RIGHT: 5px;
VERTICAL-ALIGN: middle
}

.Navigator
{
color: #003366;
}
.Navigator-Hidden
{
display:none;
}

现在运行Navi.xml的话, 您就会看见所有Layer=0的Navigator了(因为那些Layer>0的被Class="Navigator-Hidden"隐藏起来了), 当然现在还不能实现展开和折叠, 缺少toggle这个函数。



展开和折叠其实就是显示或不显示display:none or block)它与可见与不可见(visible or invisible)是有区别的, 前者不在页面预留空间。 这个toggle函数完成两个功能, 改变TR原来的Hidden属性, 使原来不显示的显示; 改变IMG的src属性, 更改图片。

toggle.js

function toggle(id)
{
var thisRow = document.all.item(id);
if (thisRow)
{
if (thisRow.getAttribute("Expanded") == 'yes')
{
thisRow.setAttribute("Expanded", "no");
thisRow.children(0).children(0).children(0).src = "images/bs.gif";

var allRows = document.all.tags("TR");
for (var i=1; i < allRows.length; i++)
{
var row = allRows[i];
if (row.getAttribute("AncestorID") == id)
{
if (row.getAttribute("Expanded") == 'yes') {
toggle(row.getAttribute("id"));
}
row.className = 'Navigator-Hidden';
}
}
thisRow.className = 'Navigator';
}
else
{
thisRow.setAttribute("Expanded", "yes");
thisRow.children(0).children(0).children(0).src = "images/bo.gif";

var allRows = document.all.tags("TR");
var depth = parseInt(thisRow.getAttribute("Depth"));
for (var i=1; i < allRows.length; i++)
{
var row = allRows[i];
if (row.getAttribute("AncestorID") == id &&
parseInt(row.getAttribute("Depth")) == depth + 1 )
{
row.className = 'Navigator';
}
}
}
}
}

到此结束。

诚然这个TOC的功能还是最基本的, 例如我还未做内容和目录的同步,其中有的地方还可以修改, 对xml和xsl文件可以进一步瘦身。 不过对一般用户来讲, 这已经足够了。
真诚希望这篇文章能对您有所启发、有所帮助, 以后做出更酷、更快、更方便、功能更强的TOC。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
XSLT是一种基于XML的转换语言,可以将XML文档转换为另一种格式。C语言可以通过使用现有的XSLT库来实现XSLT转换。下面是使用libxslt库进行XSLT转换的示例代码: ```c #include <libxslt/xslt.h> #include <libxslt/xsltutils.h> #include <libxml/xmlreader.h> int main() { xmlDocPtr doc, res; xsltStylesheetPtr sty; xmlChar *result; int size; // 加载XML文档 doc = xmlReadFile("input.xml", NULL, 0); if (doc == NULL) { fprintf(stderr, "Failed to parse input.xml\n"); return 1; } // 加载XSLT样式表 sty = xsltParseStylesheetFile("stylesheet.xslt"); if (sty == NULL) { fprintf(stderr, "Failed to parse stylesheet.xslt\n"); xmlFreeDoc(doc); return 1; } // 转换XML文档 res = xsltApplyStylesheet(sty, doc, NULL); if (res == NULL) { fprintf(stderr, "Failed to apply stylesheet\n"); xsltFreeStylesheet(sty); xmlFreeDoc(doc); return 1; } // 将结果转换成字符串 xsltSaveResultToString(&result, &size, res, sty); printf("%s", result); // 释放资源 xsltFreeStylesheet(sty); xmlFreeDoc(doc); xmlFreeDoc(res); xmlFree(result); return 0; } ``` 其中,`input.xml`是需要转换的XML文档,`stylesheet.xslt`是XSLT样式表。在代码中,首先通过`xmlReadFile`函数加载XML文档,然后通过`xsltParseStylesheetFile`函数加载XSLT样式表。接着,通过`xsltApplyStylesheet`函数将XML文档转换为目标格式。最后,通过`xsltSaveResultToString`函数将转换结果转换为字符串并输出。 需要注意的是,这里使用了libxml和libxslt库,因此需要在编译时链接这两个库。例如,在Linux系统下,可以使用以下命令进行编译: ``` gcc -o xslt_transform xslt_transform.c `xml2-config --cflags` `xml2-config --libs` -lxslt ``` 这样就可以得到一个名为`xslt_transform`的可执行文件,运行该文件即可完成XSLT转换。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值