Re: [xsl] msxml and xml:space

Subject: Re: [xsl] msxml and xml:space
From: Jeni Tennison <jeni@xxxxxxxxxxxxxxxx>
Date: Fri, 16 Aug 2002 17:56:48 +0100
Hi Simon,

> I wonder if anyone can offer a suggestion to this little problem.
> I have an XSLT template which includes the following:
>
>                          <xsl:for-each select="tabTitle">
>                                  <div class="tabTitle">
>                                  <xsl:attribute 
> name="id">title<xsl:value-of select="position()"/>DIV</xsl:attribute>
>                                  <xsl:attribute 
> name="style">position:absolute; left:<xsl:value-of
> select="40+((position()-1)*151)"/>px; top:57px; width:120px; height:46px; 
> z-index:3</xsl:attribute>
>                                          <xsl:apply-templates 
> select="./node()"/>
>                                  </div>
>                          </xsl:for-each>
>
> (sorry about any wrapping)
>
> I would expect this to generate a number of lines similar to this
> one: <div class="tabTitle" id="title1DIV" style="position:absolute;
> left:40px; top:57px; width:120px; height:46px; z-index:3">Hospital
> access</div>
>
> ...and this seems to be the case most of the time.
>
> However, when my <xsl:template> tag includes the
> xml:space="preserve" attribute, neither of the <xsl:attribute/> tags
> from the main body of the template are acted apon and the DIVs come
> out w/out the attributes.
>
> I'd like to be able to preserve the document spacing, so does anyone
> have any ideas?

Coo, interesting twist. When you put xml:space="preserve" on your
xsl:template element, you're saying "all the whitespace in this
template is significant". It's just as if you wrapped the whitespace
in xsl:text elements:

  <xsl:for-each select="tabTitle"><xsl:text>
    </xsl:text><div class="tabTitle"><xsl:text>
      </xsl:text><xsl:attribute name="id">title<xsl:value-of
select="position()"/>DIV</xsl:attribute><xsl:text>
      </xsl:text><xsl:attribute name="style">position:absolute; left:<xsl:value-of
select="40+((position()-1)*151)"/>px; top:57px; width:120px; height:46px;
z-index:3</xsl:attribute><xsl:text>
      </xsl:text><xsl:apply-templates select="./node()"/><xsl:text>
    </xsl:text></div><xsl:text>
  </xsl:text></xsl:for-each>

(Note that this is preserving the whitespace from the *stylesheet*
rather than the whitespace from the *source document*; I'm not sure
whether that's what you want to do [it's a pretty rare thing to want
to do!]).

The trouble is that you can't add content to an element before you add
attributes to that element -- if you try to then (as you've found) the
attributes are ignored. Whitespace counts as content.

A good solution, given that your attributes are pretty simple, is to
use attribute value templates rather than xsl:attribute. Try:

  <xsl:for-each select="tabTitle">
    <div class="tabTitle"
         id="title{position()}DIV"
         style="position:absolute; left:{40+((position()-1)*151)}px;
                top:57px; width:120px; height:46px; z-index:3">
      <xsl:apply-templates />
    </div>
  </xsl:for-each>

See how the xsl:value-ofs in your xsl:attributes get translated into
{...} within the attribute value?
  
[Note that I've also removed the select attribute from
xsl:apply-templates -- it applies templates to the child nodes of the
context node by default, so there's no need to specify it explicitly.]

Another (more general) solution would be to stop preserving space
within the particular div element and to use xsl:text to add the space
that you want explicitly. Something like:

  <xsl:for-each select="tabTitle">
    <div class="tabTitle" xml:space="default">
      <xsl:attribute name="id">title<xsl:value-of
select="position()"/>DIV</xsl:attribute>
      <xsl:attribute name="style">position:absolute; left:<xsl:value-of
select="40+((position()-1)*151)"/>px; top:57px; width:120px; height:46px;
z-index:3</xsl:attribute>
      <xsl:text>&#xA;    </xsl:text>
      <xsl:apply-templates select="./node()"/>
      <xsl:text>&#xA;  </xsl:text>
    </div>
  </xsl:for-each>

Or you could look into using:

  <xsl:output indent="yes" />

to make your output nicely indented (*how* nicely depends on the
processor) automatically rather than adding whitespace manually.
  
Cheers,

Jeni

---
Jeni Tennison
http://www.jenitennison.com/


 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread