我正在尝试构建一个理论上可以使用XSL无限递归的导航。不幸的是,我在这个领域的技能仍在培养中。谁能告诉我这段代码哪里出了问题?
这里的问题是,导航的前 2 个级别有些独特(类名等),但在第 3 级之后,导航几乎是一遍又一遍嵌套的相同元素。
要更新:导航级别 2 之后没有递归,级别 2 之后输出不存在,我不知道如何应用递归。我觉得这应该更容易,但我在 XSL 方面的技能不是那么好。
HTML(我们需要它产生什么):
<ul class="nav-l1">
<li class="nav-active"><a href="nav2.html" class="nav-item trigger-chan nav-next"><span><span class="trigger-cntr">First Level Parent</span></span></a>
<ul class="nav-l2 nav-hidden">
<li class="nav-active"><a href="nav2.html" class="nav-item"><span>Overview</span></a></li>
<li><a href="#" class="nav-item trigger-chan nav-next"><span><span class="trigger-cntr">Second Level Parent</span></span></a>
<ul class="nav-ls nav-hidden">
<li><a href="#" class="nav-item"><span>Third Level</span></a></li>
<li><a href="#" class="nav-item"><span>Third Level</span></a></li>
</ul>
</li>
<li><a href="#" class="nav-item"><span>Second Level</span></a></li>
</ul>
</li>
<li><a href="#" class="nav-item trigger-chan nav-next"><span><span class="trigger-cntr">First Level Parent</span></span></a>
<!-- 2nd level of navigation. -->
<ul class="nav-l2 nav-hidden">
<li><a href="#" class="nav-item"><span>Overview</span></a></li>
<li><a href="#" class="nav-item trigger-chan nav-next"><span><span class="trigger-cntr">Second Level Parent</span></span></a>
<!-- 3rd level of navigation. -->
<ul class="nav-ls nav-hidden">
<li><a href="#" class="nav-item"><span>Third Level</span></a></li>
<li><a href="#" class="nav-item"><span>Third Level</span></a></li>
<li><a href="#" class="nav-item"><span>Third Level</span></a></li>
</ul>
</li>
<li><a href="#" class="nav-item"><span>Second Level</span></a></li>
<li><a href="#" class="nav-item"><span>Second Level</span></a></li>
</ul>
</li>
</ul>
示例 XML(初始数据馈送的格式):
<data>
<folders level="1">
<folder clickable="Y" url="/lorem/ipsum.html" name="Lorem Ipsum"/>
<folder clickable="Y" url="/level/one.html" name="Level One"/>
<folder clickable="Y" url="/foo/bar.html" name="Foo Bar">
<folders level="2">
<folder clickable="Y" url="/level/two.html" name="Level two"/>
<folder clickable="Y" url="/child/item.html" name="Child Item">
<folders level="3">
<folder clickable="Y" url="/child/child/item.html" name="Child's Child Item">
<folders level="4">
<folder clickable="Y" url="/destiny/child/item.html" name="Destiny's Child"/>
</folders>
</folder>
</folders>
</folder>
</folders>
</folder>
</folders>
</data>
XSL(我们需要什么来转换XML):
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:choose>
<xsl:when test="/data/folders">
<!-- NAVIGATION BEGINS HERE -->
<ul class="nav-l1">
<!-- LEVEL 1 -->
<xsl:for-each select="/data/folders[@level=1]/folder[not (@clickable ) or @clickable ='Y' ]">
<li>
<xsl:if test="@selected='Y'">
<xsl:attribute name="class">nav-active</xsl:attribute>
</xsl:if>
<a href="{@url}" class="nav-item">
<xsl:choose>
<xsl:when test="child::*">
<xsl:attribute name="class">trigger-chan nav-next</xsl:attribute>
<span>
<span class="trigger-cntr">
<xsl:value-of select="@name"/>
</span>
</span>
</xsl:when>
<xsl:otherwise>
<span><xsl:value-of select="@name" /></span>
</xsl:otherwise>
</xsl:choose>
</a>
<!-- LEVEL 2 -->
<xsl:choose>
<xsl:when test="child::*">
<ul class="nav-l2 nav-hidden">
<xsl:for-each select="folders[@level=2]/folder[not (@clickable ) or @clickable ='Y' ]">
<li>
<xsl:if test="@selected='Y'">
<xsl:attribute name="class">nav-active</xsl:attribute>
</xsl:if>
<a href="{@url}" class="nav-item">
<xsl:choose>
<xsl:when test="child::*">
<xsl:attribute name="class">trigger-chan nav-next</xsl:attribute>
<span>
<span class="trigger-cntr">
<xsl:value-of select="@name"/>
</span>
</span>
</xsl:when>
<xsl:otherwise>
<span><xsl:value-of select="@name" /></span>
</xsl:otherwise>
</xsl:choose>
</a>
<!-- LEVEL 3 and beyond ... -->
<xsl:apply-templates />
</li>
</xsl:for-each>
</ul>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</li>
</xsl:for-each>
</ul>
</xsl:when>
</xsl:choose>
</xsl:template>
<!-- Infinity and beyond (=> Level 3) -->
<xsl:template name="folder">
<ul class="nav-ls nav-hidden">
<li>
<xsl:if test="@selected='Y'">
<xsl:attribute name="class">nav-active</xsl:attribute>
</xsl:if>
<a href="{@url}" class="nav-item">
<xsl:choose>
<xsl:when test="child::*">
<xsl:attribute name="class">trigger-chan nav-next</xsl:attribute>
<span>
<span class="trigger-cntr">
<xsl:value-of select="@name"/>
</span>
</span>
</xsl:when>
<xsl:otherwise>
<span><xsl:value-of select="@name" /></span>
</xsl:otherwise>
</xsl:choose>
</a>
<xsl:apply-templates select="folders[@level>3]/folder" />
</li>
</ul>
</xsl:template>
</xsl:stylesheet>
任何帮助将不胜感激。
谢谢!
您的 XSLT 非常注重"拉动"而不是"推",并且可以使用一些重新设计来使其更具功能性(在函数式编程意义上)。
但主要错误在这里:
<!-- Infinity and beyond (=> Level 3) -->
<xsl:template name="folder">
这是声明一个"命名模板",但它从未被调用。这不是一件坏事,因为应尽可能避免使用名称/呼叫模板。
将其更改为以下内容,在使用 XSLT 中已有apply-templates
时,它应该按预期工作。
<xsl:template match="folder">
编辑:推送式方法
这可能并不完美,但有几点: * 当它们是列表时,您完全跳过<folders>
,而<folder>
是列表项。 * 您似乎复制了级别 1、2 和 3 的项目代码。 *你只制作一个3级及以上的物品,其他的都会被忽略吗?
请注意,我已经根据级别定义了我希望如何呈现<folders>
,以及如何独立渲染<folder>
。
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="folders">
<ul>
<xsl:attribute name="class">
<xsl:text>nav-l</xsl:text>
<xsl:choose>
<xsl:when test="@level>2">
<xsl:text>s nav-hidden</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@level"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:apply-templates/>
</ul>
</xsl:template>
<xsl:template match="folder">
<li>
<xsl:if test="@selected='Y'">
<xsl:attribute name="class">nav-active</xsl:attribute>
</xsl:if>
<a href="{@url}" class="nav-item">
<xsl:choose>
<xsl:when test="child::*">
<xsl:attribute name="class">trigger-chan nav-next</xsl:attribute>
<span>
<span class="trigger-cntr">
<xsl:value-of select="@name"/>
</span>
</span>
</xsl:when>
<xsl:otherwise>
<span>
<xsl:value-of select="@name"/>
</span>
</xsl:otherwise>
</xsl:choose>
</a>
<xsl:apply-templates select="folders"/>
</li>
</xsl:template>
</xsl:stylesheet>
请注意上面的模板比初始模板短,因为重复的元素被删除了。所有<folder>
的渲染都相同,因此只需要一个模板。事实上,<folder>
不需要知道它们的级别,只有父级<folders>
知道,以便逻辑移动到正确的模板。
刚接触 XSLT 的人经常忽略如何有效地使用apply-templates
来定义递归并允许元素之间非常清晰的分离。创建模板时,请尝试关注应如何表示此特定元素,并了解如何需要其子元素。然后,重点关注子元素的新模板以及应如何呈现这些模板等。
除此之外,请始终注意@select
s 或 <value-of>
s 中的../
,因为这意味着您的逻辑依赖于当前"范围"之外的元素。并不总是坏事,但需要注意一些事情。
尝试尽可能减少for-each
,看看是否可以重写它们以使用apply-templates
,除非出于某种奇怪的原因绝对需要,否则尽量不要使用call-templates
因为它们的功能较差。