Microsoft XML SDK 2.6 - XSLT Developer's Guide

Placing a List of Items into a Grid

A frequently requested transform is placing a list of items into a grid or table. For instance, given a list of product names, you might want to present them two across in a table, like this:

product 1 product 2
product 3 product 4

Items need to be grouped in pairs into table rows. XSLT currently does not have a built-in mechanism to handle arbitrary grouping. A combination of the <xsl:if> element with the test attribute and the <xsl:variable> element can be used to approximate this behavior. The following template turns a list of "product" elements into a two-column table.

<TABLE BORDER="1">
  <xsl:for-each select="products/product">
    <xsl:variable name="pos" select="position()"/>
    <xsl:if test="position() mod 2 = 1">
      <TR>
        <TD><xsl:value-of select="."/></TD>
        <TD><xsl:value-of select="../product[$pos+1]"/></TD>
      </TR>
    </xsl:if>
  </xsl:for-each>
</TABLE>

How does this work? First, you iterate through each product element but use an <xsl:if> with the expression "position() mod 2 = 1" to isolate only those products that should start a new row (every other product) and its position saved in the variable "pos". The name of this product also is placed in the first cell in the row. Then you make another cell and place within it the next child—the one filtered out using the <xsl:if>. The query to select the next element navigates to the parent and selects the product with a position one greater than the position of the current node.

Three-row tables can be obtained by adding another cell to the row to obtain another sibling element, and adjusting the expression to allow only every third element to create a new row. You can extrapolate this mechanism to create tables with even more columns.

<TABLE BORDER="1">
  <xsl:for-each select="products/product">
    <xsl:variable name="pos" select="position()"/>
    <xsl:if test="(position() mod 3) = 1">
      <TR>
        <TD><xsl:value-of select="."/></TD>
        <TD><xsl:value-of select="../product[$pos+1]"/></TD>
        <TD><xsl:value-of select="../product[$pos+2]"/></TD>
      </TR>
    </xsl:if>
  </xsl:for-each>
</TABLE>

Try it!   View a list of products in a two- or three-column table in the Product List sample found at XSLT Developer's Guide Samples.

See the XSL version of this sample at XSL Developer's Guide Samples.