XSLT:在一个div中每隔3个div换行



我想得到一些XSLT(用于Umbraco CMS)的正确编码,但我有点被难住了。我想做的是:

从某个节点开始,将每个子节点放入一个div中;对于每3个孩子,包装在一个父类中。

我尝试实现了apply-template结构,但似乎无法掌握它的窍门,而不是我混乱的for-eachchoosewhen语句;所以现在我的XSLT一团糟(我确信这是一种糟糕的做法,对性能来说很糟糕,但我现在真的不知道该怎么办):

<div class="row four">
<h2>Smart Phones <a href="#" class="see-all">see all smart phones &rarr;</a></h2>
<div class="row three"> <!-- This div should be created again for every 3 divs -->
<xsl:for-each select="umbraco.library:GetXmlNodeById('1063')/descendant::*[@isDoc and string(showInMainNavigation) = '1']">
<xsl:choose>
<xsl:when test="position() &lt; 3">
<div class="col">
<a href="{umbraco.library:NiceUrl(./@id)}">
<img class="phonePreviewImg" src="{./previewImage}" style="max-width:117px; max-height:179px;" />
<h4 class="phoneTitle"><xsl:value-of select="./@nodeName" />/h4>
<p class="phonePrice">$<xsl:value-of select="./price" /></p
</a>
</div>
</xsl:when>
<xsl:when test="position() = 3"> <!-- set this div to include class of `omega` -->
<div class="col omega">
<a href="{umbraco.library:NiceUrl(./@id)}">
<img class="phonePreviewImg" src="{./previewImage}" style="max-width:117px; max-height:179px;" />
<h4 class="phoneTitle"><xsl:value-of select="./@nodeName" />/h4>
<p class="phonePrice">$<xsl:value-of select="./price" /></p
</a>
</div>
</xsl:when>                                                                                
</xsl:choose>
</xsl:for-each>
</div> <!-- End Row Three -->
</div> <!-- End Row Four -->

显然,这段代码不会产生"每三次包装"。有人能告诉我完成这项任务需要做些什么吗?

更新-改进了答案

我想不出一个使用模板的优雅解决方案,但这个有循环的笨拙解决方案很有效:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template name="render">
<xsl:param name="node"/>
<xsl:param name="last"/>
<div>
<xsl:if test="$last">
<xsl:attribute name="class">
<xsl:text>omega</xsl:text>
</xsl:attribute>
</xsl:if>
<xsl:value-of select="$node"/>
</div>
</xsl:template>
<xsl:template match="/*">
<root>
<xsl:variable name="nodes" select="*[not(@skip)]"/>
<xsl:for-each select="$nodes">
<xsl:if test="(position() mod 3)=1">
<xsl:variable name="position" select="position()"/>
<div>
<xsl:call-template name="render">
<xsl:with-param name="node" select="."/>
<xsl:with-param name="last" select="false()"/>
</xsl:call-template>
<xsl:if test="$nodes[$position+1]">
<xsl:call-template name="render">
<xsl:with-param name="node" select="$nodes[$position+1]"/>
<xsl:with-param name="last" select="false()"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="$nodes[$position+2]">
<xsl:call-template name="render">
<xsl:with-param name="node" select="$nodes[$position+2]"/>
<xsl:with-param name="last" select="true()"/>
</xsl:call-template>
</xsl:if>
</div>
</xsl:if>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>

应用于:

<root>
<node>1</node>
<node skip="1">to be skipped</node>
<node>2</node>
<node>3</node>
<node skip="1">to be skipped</node>
<node skip="1">to be skipped</node>
<node>4</node>
<node skip="1">to be skipped</node>
<node>5</node>
<node>6</node>
<node>7</node>
<node skip="1">to be skipped</node>
</root>

生产:

<root>
<div>
<div>1</div>
<div>2</div>
<div class="omega">3</div>
</div>
<div>
<div>4</div>
<div>5</div>
<div class="omega">6</div>
</div>
<div>
<div>7</div>
</div>
</root>

您需要将用于设置$nodes变量的select(选择所需节点的XPath)和render模板替换为生成每个节点所需结果所需的代码。

如此简单和简短

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="num[position() mod 3 = 1]">
<div>
<xsl:apply-templates mode="inGroup"
select=".|following-sibling::*[not(position() >2)]"/>
</div>
</xsl:template>
<xsl:template match="num" mode="inGroup">
<p><xsl:apply-templates mode="inGroup"/></p>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>

当此转换应用于以下XML文档时:

<nums>
<num>01</num>
<num>02</num>
<num>03</num>
<num>04</num>
<num>05</num>
<num>06</num>
<num>07</num>
<num>08</num>
<num>09</num>
<num>10</num>
</nums>

生成所需的正确结果:

<div>
<p>01</p>
<p>02</p>
<p>03</p>
</div>
<div>
<p>04</p>
<p>05</p>
<p>06</p>
</div>
<div>
<p>07</p>
<p>08</p>
<p>09</p>
</div>
<div>
<p>10</p>
</div>

这里有一个使用模板的优雅解决方案。

当此XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:param name="pNumInGroup" select="3" />
<xsl:template match="/*">
<html>
<xsl:apply-templates select="*[position() mod $pNumInGroup = 1]" />
</html>
</xsl:template>
<xsl:template match="node">
<div>
<xsl:for-each
select=".|following-sibling::*[not(position() &gt; $pNumInGroup - 1)]">
<div>
<xsl:apply-templates />
</div>
</xsl:for-each> 
</div>
</xsl:template>
</xsl:stylesheet>

。。。应用于@MiMo提供的示例XML:

<root>
<node>1</node>
<node>2</node>
<node>3</node>
<node>4</node>
<node>5</node>
<node>6</node>
<node>7</node>
</root>

。。。生成正确的结果:

<html>
<div>
<div>1</div>
<div>2</div>
<div>3</div>
</div>
<div>
<div>4</div>
<div>5</div>
<div>6</div>
</div>
<div>
<div>7</div>
</div>
</html>

如果参数值更改为5:

<xsl:param name="pNumInGroup" select="5" />

。。。仍然会产生正确的结果:

<html>
<div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
</div>
<div>
<div>6</div>
<div>7</div>
</div>
</html>

解释:

  • 我们在文档顶部定义了一个pNumInGroup参数(默认值为3)。这很有用,因为它允许XSLT更灵活地使用(即,如果每个组需要不同数量的<div>元素,只需将它们作为参数传递即可)
  • 第一个模板匹配根节点,重新创建它,并告诉XSLT处理器将模板应用到每个分组的第一个元素(如果需要,这里将复习模块化算法)
  • 第二个模板与上一个模板选择的<node>元素相匹配。对于每一个,都会创建一个新的<div>元素,并用适合该包装组的剩余项目填充

注意:我通常远离<xsl:for-each>,除非我真的需要它;在最后一个模板的情况下,我并不真的需要它(我可以很容易地用另一个模板指定包装/辅助<div>逻辑)。然而,为了"清晰",并且不过度模板化XSLT,我选择了这条路线。

最新更新