locked
XSLT - Handling empty values (element/attribute etc) - IE tables issue RRS feed

  • Question

  • Hi all,

    has anyone in the past used a work around for outputting empty values into a HTML table via XSLT?

    For some reason, IE doesn't render a table cell (<TD>) when no data is put into that cell, and I am generating a results table from an XML file, and not every cell will contain a value (Which I obtain via <xsl:value-of select="info/@value" />).

    Is there a clean way for me to insert a &nbsp; value into these cells, without having to really perform checks on each XML element value/attribute I pull out of the XML document?

    Thanks
    Tryst
    Tuesday, April 21, 2009 11:27 PM

Answers

  • The problem exists with browsers and HTML in general, whether you use XSLT to generate the HTML does not matter.
    The clean solution is the use a CSS stylesheet with http://www.w3.org/TR/CSS21/tables.html#empty-cells e.g.
    table { empty-cells: show; }

    However the problem with that clean solution and IE is that only IE 8 supports that property empty-cells.

    Thus to cover earlier versions of IE you need to insert a non-breaking space with &#160;. I am afraid there is no short way to do that with XSLT, with XSLT 2.0 you could write your own function and then call that in the select expression. With XSLT 1.0 you can write a named template but as you can't call that from an XPath expression you then have to call it with xsl:call-template which is rather verbose e.g.

    <xsl:template name="make-cell">
      <xsl:param name="nodes"/>
      <td>
      <xsl:choose>
         <xsl:when test="normalize-space($nodes)">
           <xsl:value-of select="$nodes"/>
        </xsl:when>
        <xsl:otherwise>
           <xsl:text>&#160;</xsl:text>
        </xsl:otherwise>
      </xsl:choose>
      </td>
    </xsl:template>
    
    <xsl:template match="row">
      <tr>
        <xsl:for-each select="*">
          <xsl:call-template name="make-cell">
             <xsl:with-param name="nodes" select="."/>
          </xsl:call-template>
       </xsl:for-each>
      </tr>
    </xsl:template>


    MVP XML My blog
    Wednesday, April 22, 2009 10:24 AM

All replies

  • Can you just simply append &nbsp; into every cell? So that if the XML node contians a value, you will get "value + space" in the table cell, otherwise you get "space" in the table cell.

    <td><xsl:value-of select="info/@value"/>&nbsp;</td>

    Your potential, our passion.
    Wednesday, April 22, 2009 12:47 AM
  • The problem exists with browsers and HTML in general, whether you use XSLT to generate the HTML does not matter.
    The clean solution is the use a CSS stylesheet with http://www.w3.org/TR/CSS21/tables.html#empty-cells e.g.
    table { empty-cells: show; }

    However the problem with that clean solution and IE is that only IE 8 supports that property empty-cells.

    Thus to cover earlier versions of IE you need to insert a non-breaking space with &#160;. I am afraid there is no short way to do that with XSLT, with XSLT 2.0 you could write your own function and then call that in the select expression. With XSLT 1.0 you can write a named template but as you can't call that from an XPath expression you then have to call it with xsl:call-template which is rather verbose e.g.

    <xsl:template name="make-cell">
      <xsl:param name="nodes"/>
      <td>
      <xsl:choose>
         <xsl:when test="normalize-space($nodes)">
           <xsl:value-of select="$nodes"/>
        </xsl:when>
        <xsl:otherwise>
           <xsl:text>&#160;</xsl:text>
        </xsl:otherwise>
      </xsl:choose>
      </td>
    </xsl:template>
    
    <xsl:template match="row">
      <tr>
        <xsl:for-each select="*">
          <xsl:call-template name="make-cell">
             <xsl:with-param name="nodes" select="."/>
          </xsl:call-template>
       </xsl:for-each>
      </tr>
    </xsl:template>


    MVP XML My blog
    Wednesday, April 22, 2009 10:24 AM
  • Hi both, and thanks for the replies.

    Martin, so that final solution you posted (with Templates etc), is this like standard practice, as I know this must be a big issue with styling tabular data from XML?
    Tryst
    Wednesday, April 22, 2009 2:47 PM
  • For XSLT 1.0 a named template and calling that is standard practice, yes, the same like encapsulating code in a method or subroutine.

    Of course, depending on your needs you might not even need a named template and call-template, it could suffice to use e.g.

    <xsl:template match="row">
      <tr>
        <xsl:apply-templates/>
      </tr>
    </xsl:template>
    
    <xsl:template match="row/*">
      <td>
         <xsl:choose>
            <xsl:when test="normalize-space(.)">
               <xsl:value-of select="."/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:text>&#160;</xsl:text>
            </xsl:otherwise>
         </xsl:choose>
      </td>
    </xsl:template>
    which is a bit easier.



    MVP XML My blog
    Wednesday, April 22, 2009 5:25 PM
  • HI Martin,

    coming back to this, is using the XSLT function 'normalize-space()' the best way to test if a value exists in element/attribute?

    Previously I have used 'text()' to see if a value exists in an element.

    Thanks
    Tryst
    Tuesday, May 12, 2009 5:26 PM
  • Well for the problem we have in this thread we want to check whether there is any non whitespace character inside the element so normalize-space is necessary.
    If you did <xsl:when test="text()"> then that would return true even if there is nothing but whitespace in the element text and that would not help with the original problem of preventing the problem of browser like IE not rendering table cells containing nothing but whitespace.

    Thus as with most issues what is the best way depends on the goal you have. If you want to test whether an element has any child nodes you could test <xsl:if test="node()">, if you want to test whether it has any text child nodes then <xsl:if test="text()"> is the right approach.

    As for attribute nodes, in the XSLT/XPath data model they don't have any children at all so for attributes an test="text()" would not make sense.
    MVP XML My blog
    Tuesday, May 12, 2009 5:41 PM