在周未的时间里,忙了两个小时,终于把示例代码写完了,虽然很累,但却很开心,因为我知道这个示例代码将会给很多人带来启发,它是有价值的,它的价值也将体现我的价值,不是么?
在你看这篇文章之前,我想唠叨几句,我希望你在看文章也好,看示例代码也好,着重是看其中的思想与技巧,举一反三,而不是直接拿代码就去使用了。或许你会有更好的实现方法,没关系,我希望我这篇文章能起到抛砖引玉的作用,让我们一起进步。看这篇文章,你可能需要具备一些基础的知识,如Javascript、CSS、Xslt、Xml,当然这不是必需的,如果你悟性比较高的话。闲话少讲,言归正传,follow me!让我们步入Xslt神奇的殿堂吧
首先,我们应该了解一下需求:
1、要求在一个页面中有若干个标签块(即是由若干个标签组成的区域),并且通过Xslt格式化Xml
2、具备扩展性,标签及签块可以在xml中自由添加
3、鼠标移至不同的标签,会显示相应的内容
我们知道,程序最重要的是在规划,在做之前我们也应该要规划一下,我初步实现的思路是这样的,用xml描述布局、标签块及标签的信息,通过xslt循环布局信息,然后再循环标签块信息,再循环标签及内容信息。
这样就需要经过三层循环,最后一层循环需要两次循环,第一次是循环标签标题,第二次是循环标签内容。最后一层循环还需要做一个事情调用一个javascript函数,把当前标签块及内容块的元素ID,以便注册事件,关于这个函数的详细说明,请参考拙作
用DIV+Javascript实现标签功能
现在我们重点来规划一下xml,xml的结构是否合理,直接影响到xslt的编写,首先我们看一下xml的基本结构,然后根据示例再作说明
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="index.xsl" ?>
<Root>
<!--栏目列表-->
<Columus Class="LeftSide">
<!--标签块列表-->
<Labels>
<!--相应的标签-->
<Label>
<Caption><![CDATA[标签1]]></Caption>
<Content><![CDATA[内容1]]></Content>
</Label>
<!--更多标签...-->
</Labels>
<!--更多的标签块...-->
</Columus>
<!--更多栏目...-->
</Root>
我们首先将页面采用三分栏的布局,用Columus节点来描述布局信息,并增加一个Class的属性;在每一个分栏中,会有若干个标签块,用Labels节点来描述标签块;在每一个标签块中,会有若干个标签,用Label节点来描述;而在每一个标签中,又会由一个标签标题及内容组成,我们用Caption描述标签标题,用Content描述内容信息。
现在我们看看xml的结构,是不是很简洁,如果需要再添加标签或者标签块,我只需要在相应的位置添加描述信息就可以了,比如说,我要第一栏的第一个标签块中增加一个标签,就可以这样写:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="index.xsl" ?>
<Root>
<Columus Class="LeftSide">
<Labels>
<Label>
<Caption><![CDATA[标签1]></Caption>
<Content><![CDATA[内容1]]></Content>
</Label>
<!--添加Label节点,就可以实现添加标签-->
<Label>
<Caption><![CDATA[这是增加的标签]></Caption>
<Content><![CDATA[内容2]]></Content>
</Label>
</Labels>
</Columus>
</Root>
xml结构完成了,也就成功了一半(其实还有一个重要的工作是javascript,因为javascript在另一个例子已经讲过了,所以不用再编写了),接下来该轮到xslt这位兄弟粉墨登场了,核心代码如下:
<!--第一层循环,循环出布局信息-->
<xsl:for-each select="Root/*">
<xsl:variable name="_Class" select="@Class" />
<!--将class信息添加到div中-->
<div class="{$_Class}">
<!--这个地方很重要,因为我们要每一个标签组取一个全局唯一的id名称,所以需要两次用到position()这个属性来确定唯一的名称-->
<xsl:variable name="_LabelsIndex" select="position()" />
<!--第二层循环,循环出标签组信息-->
<xsl:for-each select="Labels">
<!--再次取当前的索引信息,用上一层的索引信息与本层的索引信息来确定唯一的名称-->
<xsl:variable name="Index"><xsl:value-of select="$_LabelsIndex" /><xsl:value-of select="position()" /></xsl:variable>
<xsl:variable name="LabelName">Label<xsl:value-of select="$Index" /></xsl:variable>
<xsl:variable name="ContainerName">Container<xsl:value-of select="$Index" /></xsl:variable>
<!--标签-->
<ul id="{$LabelName}" class="Labels">
<!--第三层循环的第一个循环,循环出标签标题信息,只所以在第三层要做两次循环,是因为我们要将标签信息和内容信息分别放在两个不同的容器中-->
<xsl:for-each select="Label/Caption">
<li>
<!--如果当前是索引是第一个,则添加Currentlabel这个class-->
<xsl:if test="position() = 1">
<xsl:attribute name="class">Currentlabel</xsl:attribute>
</xsl:if>
<!--.相当于current()这个函数,就是列出当前节点的内容-->
<xsl:value-of select="." disable-output-escaping="yes"/>
</li>
</xsl:for-each>
</ul>
<!--当前标签组的内容组容器,一定要有id,因为javascript需要通过这个id循环子节点,以获取不同的内容-->
<div id="{$ContainerName}" class="Container">
<!--第三层循环的第二个循环,循环出标签内容信息-->
<xsl:for-each select="Label/Content">
<div>
<!--如果当前是索引不是第一个,则用display:none这个属性将其隐藏-->
<xsl:if test="position() != 1">
<xsl:attribute name="style">display: none</xsl:attribute>
</xsl:if>
<xsl:value-of select="." disable-output-escaping="yes"/>
</div>
</xsl:for-each>
</div>
<!--调用RegisterEvent为所有的标签注册事件-->
<script language="javascript">
RegisterEvent ("<xsl:value-of select="$LabelName" />", "<xsl:value-of select="$ContainerName" />");
</script>
</xsl:for-each>
</div>
</xsl:for-each>
这里的重点是一是多次用到了position()这个属性,二是RegisterEvent注册事件的javascript函数。到这里就差不多接近尾声了,有一些遗憾是对firefox的支持,仅仅是因为firefox不支持disable-output-escaping="yes"的属性,但本例子重点不是兼容性的,所以我也就忽略这些了。
你可能会说:“哦,原来是这样,没什么了不起啊,我也会”,不过请不要忘记哥伦布发现美洲大陆的例子哦。其实这个例子很简单,没有什么太大的技术难度,重要的是一种思想,一些技巧,还是那句老话,会削木头并不一定能做柜子。
已有1个评论在 "用Xslt/CSS/Xml/Javascript做标签"
2010-4-23 17:20
好像回到了九几年