xmlxsltcdata

How can I split an <xsl:foreach> into multiple parts?


I have a list of elements that I want to split into individual lists of 3. The end result would be something like this:

<ul>
    <li>element</li>
    <li>element</li>
</ul>
<ul>
    <li>element</li>
    <li>element</li>
</ul>
<ul>
    <li>element</li>
    <li>element</li>
</ul>

My XSLT is like this, but it doesn't work, because I can't insert </ul>, and I can't insert a less than sign (<).

<ul>
    <xsl:for-each select="$myroot/item">
        <li></li>

        <xsl:if test="position() mod $maxItemsPerColumn = 0">
            <!-- I want to close my ul, and start a new one here, but it doesn't work! -->
        </xsl:if>
    </xsl:for-each>
</ul>

Any ideas? Thanks in advance!


Solution

  • You don't need to do anything fancy like recursion. And good lord, don't even contemplate using CDATA.

    You just have to think like XSLT and ask: "What input element do I want to transform into my output element?"

    Assuming that each ul is supposed to contain N items, you want to transform every Nth input item, starting with the first, into a ul:

    <xsl:variable name="n" select="number(4)"/>
    
    <xsl:template match="/">
      <output>
        <xsl:apply-templates select="/root/item[position() mod $n = 1]"/>
      </output>
    </xsl:template>
    

    Each of these item elements becomes a ul that contains the item and each of its N-1 following siblings:

    <xsl:template match="item">
      <ul>
        <xsl:for-each select=". | following-sibling::item[position() &lt; $n]">
          <li>
            <xsl:value-of select="."/>
          </li>
        </xsl:for-each>
      </ul>
    </xsl:template>
    

    Assuming an input document like this:

    <root>
      <item>1</item>
      <item>2</item>
      <item>3</item>
      <item>4</item>
      <item>5</item>
      <item>6</item>
      <item>7</item>
      <item>8</item>
      <item>9</item>
    </root>
    

    ...you get this output, if $n is set to 4:

    <output>
      <ul>
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
      </ul>
      <ul>
        <li>5</li>
        <li>6</li>
        <li>7</li>
        <li>8</li>
      </ul>
      <ul>
        <li>9</li>
      </ul>
    </output>