按字母顺序水平和垂直对齐文本

时间:2011-12-14 20:29:55

标签: css xslt xslt-1.0

我有几个标题,我通过循环一些xml(它可以是n号标题)得到。我希望将它们水平显示在3列中,但是按字母顺序垂直显示:

如果我有3个标题,我只用字母代表它们(我可以得到没有标题的数量。

A              B              C                    -----count(3)

4 titles:
A              C              D                    -----count(4)
B    

5 Titles: 
A              C              E                    -----count(5)
B              D   

7 Titles:
A              D              F                     -----count(7)
B              E              G
C

我正在使用xsl 1.0,现在我就像

一样
<div class="navigation">
<ul>
<xsl:foreach select="/Custom/Alphabet/titles">

   <li>
     <xsl:value-of select="." />
   </li>
</xsl:foreach>
</ul>
</div>

2 个答案:

答案 0 :(得分:4)

很棒的问题!

<强>予。这是一个XSLT 2.0解决方案(65行,几乎可以机械地转换为XSLT 1.0):

<xsl:stylesheet version="2.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:xs="http://www.w3.org/2001/XMLSchema"
     xmlns:my="my:my" exclude-result-prefixes="xs my">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>

     <xsl:variable name="vItems" select="/*/*/string(.)"
      as="item()+"/>


     <xsl:template match="/">
      <xsl:sequence select="my:fill($vItems, 3)"/>
     </xsl:template>

     <xsl:function name="my:fill" as="element()+">
      <xsl:param name="pItems" as="item()*"/>
      <xsl:param name="pK" as="xs:integer"/>

      <xsl:variable name="pN" select="count($pItems)"/>

      <xsl:choose>
       <xsl:when test="$pN le $pK">
        <xsl:sequence select="my:fillRow($pItems)"/>
       </xsl:when>
       <xsl:otherwise>
        <xsl:variable name="vColSize"
         select="ceiling($pN div $pK)"/>

        <xsl:variable name="vCol-1" select=
         "$pItems[position() le $vColSize]"/>

        <xsl:variable name="vSubTable"
         select="my:fill($pItems[position() gt $vColSize],
                         $pK -1
                        )
         "/>

         <xsl:sequence select="my:merge($vCol-1, $vSubTable)"/>
       </xsl:otherwise>
      </xsl:choose>
     </xsl:function>

     <xsl:function name="my:fillRow" as="element()">
      <xsl:param name="pItems" as="item()*"/>

      <row>
       <xsl:for-each select="$pItems">
        <cell><xsl:sequence select="."/></cell>
       </xsl:for-each>
      </row>
     </xsl:function>

     <xsl:function name="my:merge" as="element()*">
      <xsl:param name="pCol" as="item()*"/>
      <xsl:param name="pTable" as="element()*"/>

      <xsl:for-each select="$pCol">
       <xsl:variable name="vrowPos" select="position()"/>
       <row>
        <cell><xsl:sequence select="."/></cell>
        <xsl:sequence select="$pTable[position() eq $vrowPos]/cell"/>
       </row>
      </xsl:for-each>
     </xsl:function>
</xsl:stylesheet>

将此转换应用于所提供的(最复杂的)7项案例

<titles>
 <t>A</t>
 <t>B</t>
 <t>C</t>
 <t>D</t>
 <t>E</t>
 <t>F</t>
 <t>G</t>
</titles>

产生了想要的正确结果

<row>
   <cell>A</cell>
   <cell>D</cell>
   <cell>F</cell>
</row>
<row>
   <cell>B</cell>
   <cell>E</cell>
   <cell>G</cell>
</row>
<row>
   <cell>C</cell>
</row>

我已经确认每个 N = 1 to 7都会产生预期的正确结果。

说明:

我们正在构建输入序列(pN)中项目数的递归所需的表

  1. 递归的 base 适用于任何$pN不大于$pK(所需列数)。在这个基本情况下,表格只有一行。

  2. 在一般情况下$pN > $pK;然后我们构建最左边的列$vCol-1,并递归地创建一个较小的表,其中包含其余项和新的所需列数:$pK -1

  3. 在上面的情况2.我们最终合并了列和子表以生成结果表。


  4. <强> II。等效的XSLT 2.0解决方案,以“更多XSLT 2.0风格”(60行)编写:

    <xsl:stylesheet version="2.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:xs="http://www.w3.org/2001/XMLSchema"
     xmlns:my="my:my" exclude-result-prefixes="xs my">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
    
     <xsl:variable name="vItems" select="/*/*/string(.)"
      as="item()+"/>
    
    
     <xsl:template match="/">
      <xsl:sequence select="my:fill($vItems, 3)"/>
     </xsl:template>
    
     <xsl:function name="my:fill" as="element()+">
      <xsl:param name="pItems" as="item()*"/>
      <xsl:param name="pK" as="xs:integer"/>
    
      <xsl:sequence select=
       "for $vN in count($pItems)
         return
           if($vN le $pK)
             then my:fillRow($pItems)
             else
               (for $vColSize in xs:integer(ceiling($vN div $pK))
                 return
                   my:merge((for $i in 1 to $vColSize
                               return $pItems[$i]),
                                 my:fill((for $i in $vColSize+1 to $vN
                                           return $pItems[$i]),
                                          $pK -1
                                         )
                            )
                )
       "/>
     </xsl:function>
    
     <xsl:function name="my:fillRow" as="element()">
      <xsl:param name="pItems" as="item()*"/>
    
      <row>
       <xsl:for-each select="$pItems">
        <cell><xsl:sequence select="."/></cell>
       </xsl:for-each>
      </row>
     </xsl:function>
    
     <xsl:function name="my:merge" as="element()*">
      <xsl:param name="pCol" as="item()*"/>
      <xsl:param name="pTable" as="element()*"/>
    
      <xsl:for-each select="$pCol">
       <xsl:variable name="vrowPos" select="position()"/>
       <row>
        <cell><xsl:sequence select="."/></cell>
        <xsl:sequence select="$pTable[position() eq $vrowPos]/cell"/>
       </row>
      </xsl:for-each>
     </xsl:function>
    </xsl:stylesheet>
    

    <强> III。 XSLT 1.0解决方案(75行)

    这是第一个XSLT 2.0解决方案(上图),几乎机械地转换为XSLT 1.0:

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
         <xsl:output omit-xml-declaration="yes" indent="yes"/>
    
         <xsl:variable name="vItems" select="/*/*"/>
    
         <xsl:template match="/">
          <xsl:call-template name="fill">
           <xsl:with-param name="pItems" select="$vItems"/>
           <xsl:with-param name="pK" select="3"/>
          </xsl:call-template>
         </xsl:template>
    
         <xsl:template name="fill">
          <xsl:param name="pItems"/>
          <xsl:param name="pK"/>
    
          <xsl:variable name="vN" select="count($pItems)"/>
    
          <xsl:choose>
           <xsl:when test="not($vN > $pK)">
            <row>
             <xsl:call-template name="fillRow">
              <xsl:with-param name="pItems" select="$pItems"/>
             </xsl:call-template>
            </row>
           </xsl:when>
           <xsl:otherwise>
            <xsl:variable name="vColSize"
             select="ceiling($vN div $pK)"/>
    
            <xsl:variable name="vCol-1" select=
             "$pItems[not(position() > $vColSize)]"/>
    
            <xsl:variable name="vrtfSubtable">
             <xsl:call-template name="fill">
              <xsl:with-param name="pItems" select=
               "$pItems[position() > $vColSize]"/>
              <xsl:with-param name="pK" select="$pK -1"/>
             </xsl:call-template>
            </xsl:variable>
    
            <xsl:variable name="vSubTable" select=
            "ext:node-set($vrtfSubtable)/*"/>
    
             <xsl:call-template name="merge">
              <xsl:with-param name="pCol" select="$vCol-1"/>
              <xsl:with-param name="pTable" select="$vSubTable"/>
             </xsl:call-template>
           </xsl:otherwise>
          </xsl:choose>
         </xsl:template>
    
         <xsl:template name="fillRow">
          <xsl:param name="pItems"/>
    
           <xsl:for-each select="$pItems">
            <cell><xsl:value-of select="."/></cell>
           </xsl:for-each>
         </xsl:template>
    
         <xsl:template name="merge">
          <xsl:param name="pCol"/>
          <xsl:param name="pTable"/>
    
          <xsl:for-each select="$pCol">
           <xsl:variable name="vrowPos" select="position()"/>
           <row>
            <cell><xsl:value-of select="."/></cell>
            <xsl:copy-of select="$pTable[position() = $vrowPos]/cell"/>
           </row>
          </xsl:for-each>
         </xsl:template>
    </xsl:stylesheet>
    

    <强> IV。最后,一个纯粹的,生成性的(非递归的)XSLT 1.0解决方案:

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
    
     <xsl:param name="pK" select="3"/>
    
     <xsl:variable name="vItems" select="/*/*"/>
    
     <xsl:template match="/">
      <xsl:call-template name="genTable"/>
     </xsl:template>
    
     <xsl:template name="genTable">
      <xsl:param name="pItems" select="$vItems"/>
      <xsl:param name="pK" select="$pK"/>
    
      <xsl:variable name="vN" select=
       "count($vItems)"/>
      <xsl:variable name="vnumRows"
         select="ceiling($vN div $pK)"/>
    
      <table>
       <xsl:for-each select=
         "$pItems[not(position() > $vnumRows)]">
         <xsl:call-template name="genRow">
          <xsl:with-param name="pRowInd" select="position()"/>
          <xsl:with-param name="pItems" select="$vItems"/>
          <xsl:with-param name="pK" select="$pK"/>
         </xsl:call-template>
       </xsl:for-each>
      </table>
     </xsl:template>
    
     <xsl:template name="genRow">
      <xsl:param name="pRowInd" select="position()"/>
      <xsl:param name="pItems" select="$vItems"/>
      <xsl:param name="pK" select="$pK"/>
    
      <xsl:variable name="vN" select=
       "count($vItems)"/>
      <xsl:variable name="vFullCols" select=
       "$vN mod $pK"/>
      <xsl:variable name="vFullColSize" select=
       "ceiling($vN div $pK)"/>
    
       <tr>
         <td><xsl:value-of select="$pItems[number($pRowInd)]"/></td>
    
       <xsl:for-each select=
        "$pItems[position() > 1
               and
                 not(position() > $pK)
                ]">
        <xsl:variable name="vX" select="position()+1"/>
    
        <xsl:variable name="vMinFullColsAndX" select=
        "($vX > $vFullCols) * $vFullCols
         +
          not($vX > $vFullCols) * $vX
        "/>
    
        <xsl:variable name="vAmmt1" select=
        "$vMinFullColsAndX * $vFullColSize
        "/>
    
        <xsl:variable name="vAmmt2" select=
        "($vX -1 - $vMinFullColsAndX) * ($vFullColSize -1)
        "/>
    
        <xsl:variable name="vValue" select=
        "$vAmmt1 + $vAmmt2"/>
    
        <xsl:if test="not(($pRowInd -1) * $pK +$vX > $vN)">
         <td><xsl:value-of select=
         "$pItems[position()=$pRowInd+$vValue]"/>
         </td>
        </xsl:if>
       </xsl:for-each>
      </tr>
    
     </xsl:template>
    </xsl:stylesheet>
    

    应用于同一XML文档(上图)时,会生成所需的正确结果

    <table>
       <tr>
          <td>A</td>
          <td>D</td>
          <td>F</td>
       </tr>
       <tr>
          <td>B</td>
          <td>E</td>
          <td>G</td>
       </tr>
       <tr>
          <td>C</td>
       </tr>
    </table>
    

答案 1 :(得分:1)

CSS3实际上定义了一个漂亮的属性,为您执行此操作,称为column-count。在良好的浏览器和IE10中它是supported。如果向后兼容性不是问题,这是一个非常好的解决方案,接近你想要的。

http://jsfiddle.net/CMeXC/