我正在尝试构建一个理论上可以使用XSL无限递归的导航。不幸的是,我在这个领域的技能仍在建设中。谁能告诉我这个代码我哪里出错?
这里的踢球者是导航的前两个级别有点独特(类名等)但是在级别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(初始数据Feed的格式):
<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>
非常感谢任何帮助。
谢谢!
答案 0 :(得分:3)
您的XSLT非常注重"pull" focused rather than "push",可以使用一些重新设计来实现功能(在函数式编程意义上)。
但主要错误在于:
<!-- Infinity and beyond (=> Level 3) -->
<xsl:template name="folder">
这是宣布"named template" but it's never called。这不是一件坏事,如name/call-templates should be avoided where possible.
将其更改为以下内容,并且在使用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
中的<value-of>
,因为这意味着您的逻辑依赖于当前的元素“范围”。并不总是坏事,但需要注意的事项。
尽可能尝试并尽量减少for-each
并查看是否可以重写它们以使用apply-templates
,除非出于某种奇怪的原因绝对需要,否则请尝试从不使用call-templates
因为它们不那么< EM>功能