Avatar of colly92002
colly92002
Flag for United Kingdom of Great Britain and Northern Ireland asked on

!CDATA <TR> tags are being converted to >TR< (XSLT)

Hello.

When my XML contains a node called "new_line", I would like to my XSLT to write out hte HTMl to close the table row and open a new one.

Since the parse does not like this (I know I am being naughty) I came up with the following:

            <xsl:when test = "name(.) = 'new_line'">
                <!--Need to break up the line -->

                  <![CDATA[
                  </TR>
                  <TR>
                  ]]>

            </xsl:when>
Unfortunately this converts the < and > into "&lt;" and "&gt;"

I tried wrapping it in <script> tags which does not convert it, but then the browser ignores the tags!

Any ideas?

Thanks.

(P.S. ideally (I think) I should develop some kind of xlst that will take take any XML convert this into further XML with additional sub-nodes <LINE1> <LINE2> etc, depending upto the the contents of the XML (i.e. where it finds "new_line" nodes).  My XSLT could then call templates for each subnode.  However I don't know how to do this but if you could point me in the direction of a tutorial I would be most grateful!).
Web Languages and StandardsXML

Avatar of undefined
Last Comment
Gertone (Geert Bormans)

8/22/2022 - Mon
Gertone (Geert Bormans)

Hi colly92002,

Here is what you could do
  <xsl:text disable-output-escaping="yes">&lt;/tr>&lt;tr></xsl:text>

but this is highly not recommended

You should always try to find a solution that is wellformed
You introduce a very procedural way of programming that can cause
maintenace issues and guarantueed bugs in XSLT

The XSLT way would be to process all the nodes between two <new_line/> elements
inside a <tr></tr>

I will post an example in a minute

Cheers!
Gertone (Geert Bormans)

colly92002,

with this XML
<?xml version="1.0" encoding="UTF-8"?>
<test>
    <cell>A1</cell>
    <cell>A2</cell>
    <cell>A3</cell>
    <new_line/>
    <cell>B1</cell>
    <cell>B2</cell>
    <cell>B3</cell>
    <new_line/>
    <cell>C1</cell>
    <cell>C2</cell>
    <cell>C3</cell>
    <new_line/>
    <cell>D1</cell>
    <cell>D2</cell>
    <cell>D3</cell>
</test>

this XSLT would create the table rows, without reverting to output escaping tricks

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:template match="/">
        <html>
            <body>
                <xsl:apply-templates/>
            </body>
        </html>
    </xsl:template>
    <xsl:template match="test">
        <table border="1">
            <xsl:for-each select="new_line">
                <xsl:choose>
                    <!-- if only one new_line -->
                    <xsl:when test="not(preceding-sibling::new_line) and not(following-sibling::new_line)">
                        <tr><xsl:apply-templates select="preceding-sibling::*"/></tr>
                        <tr><xsl:apply-templates select="following-sibling::*"/></tr>
                    </xsl:when>
                    <!-- if the first newline -->
                    <xsl:when test="not(preceding-sibling::new_line)">
                        <tr><xsl:apply-templates select="preceding-sibling::*"/></tr>
                    </xsl:when>
                    <!-- if the last new_line -->
                    <xsl:when test="not(following-sibling::new_line) ">
                        <xsl:variable name="prevNodePos" select="count(preceding-sibling::new_line[1]/preceding-sibling::*)"/>
                        <xsl:variable name="thisNodePos" select="count(preceding-sibling::*)"/>
                        <tr><xsl:apply-templates select="preceding-sibling::*[count(preceding-sibling::*) &gt; $prevNodePos]"/></tr>
                        <tr><xsl:apply-templates select="following-sibling::*"/></tr>
                    </xsl:when>
                    <!-- all other new_line -->
                    <xsl:otherwise>
                        <xsl:variable name="prevNodePos" select="count(preceding-sibling::new_line[1]/preceding-sibling::*)"/>
                        <xsl:variable name="thisNodePos" select="count(preceding-sibling::*)"/>
                         <tr><xsl:apply-templates select="preceding-sibling::*[count(preceding-sibling::*) &gt; $prevNodePos]"/></tr>
                    </xsl:otherwise>
                </xsl:choose>
               
            </xsl:for-each>
        </table>
    </xsl:template>
    <xsl:template match="cell">
        <td><xsl:value-of select="."/></td>
    </xsl:template>
</xsl:stylesheet>

This is a quick hack,
you could easily develop something nicer using recursion,
but I just wanted to get the idea accross

cheers

Geert
colly92002

ASKER
Thank you so much for your time!

I will try and build the *proper* answer into my application tomorrow, although I may need a little more help appying it to my current xslt/XML as each "cell" has an unique name (the column name from the the SQL that creates the XML - other than that I can see where you are coming from).

Thanks again!

Iain.
All of life is about relationships, and EE has made a viirtual community a real community. It lifts everyone's boat
William Peck
Gertone (Geert Bormans)

don't hesitate to post part of your XML/XSLT if you need help
cheers
colly92002

ASKER
I think I am starting to get to grips with the modular XML stuff now but I still struggle to break out of my procedural habits.

I have modified your XSLT to my application and have nearly got it working.

Here is the XML (note using randomised example data):
- <selectrows>
- <selectrow>
  <recordnumber>0</recordnumber>
  <fnam>Paul</fnam>
  <snam>Walker</snam>
  <adm>01/03/2003</adm>
  <new_line>new_line</new_line>
  <name>Stonnar Ward</name>
  <age>35</age>
  <dob>14/09/1967</dob>
  <new_line>new_line</new_line>
  </selectrow>
- <selectrow>
  <recordnumber>1</recordnumber>
  <fnam>Richard</fnam>
  <snam>Dillon</snam>
  <adm>23/04/2003</adm>
  <new_line>new_line</new_line>
  <name>Stonnar Ward</name>
  <age>30</age>
  <dob>18/03/1973</dob>
  <new_line>new_line</new_line>
  </selectrow>
- <selectrow>
  <recordnumber>2</recordnumber>
  <fnam>Jeffrey</fnam>
  <snam>Davies</snam>
  <adm>25/04/2003</adm>
  <new_line>new_line</new_line>
  <name>Stonnar Ward</name>
  <age>30</age>
  <dob>23/05/1973</dob>
  <new_line>new_line</new_line>
  </selectrow>
- <selectrow>
  <recordnumber>3</recordnumber>
  <fnam>Caroline</fnam>
  <snam>Thomas</snam>
  <adm>03/01/2003</adm>
  <new_line>new_line</new_line>
  <name>Stonnar Ward</name>
  <age>31</age>
  <dob>02/02/1972</dob>
  <new_line>new_line</new_line>
  </selectrow>
- <selectrow>
  <recordnumber>4</recordnumber>
  <fnam>Sue</fnam>
  <snam>Bextor</snam>
  <adm>20/05/2003</adm>
  <new_line>new_line</new_line>
  <name>Stonnar Ward</name>
  <age>20</age>
  <dob>27/11/1982</dob>
  <new_line>new_line</new_line>
  </selectrow>
- <selectrow>
  <recordnumber>5</recordnumber>
  <fnam>Helena</fnam>
  <snam>Bartholemew</snam>
  <adm>01/05/2003</adm>
  <new_line>new_line</new_line>
  <name>Stonnar Ward</name>
  <age>16</age>
  <dob>08/08/1986</dob>
  <new_line>new_line</new_line>
  </selectrow>
- <selectrow>
  <recordnumber>6</recordnumber>
  <fnam>Stephen</fnam>
  <snam>Hickenbottom</snam>
  <adm>12/01/2003</adm>
  <new_line>new_line</new_line>
  <name>Stonnar Ward</name>
  <age>25</age>
  <dob>07/01/1978</dob>
  <new_line>new_line</new_line>
  </selectrow>
- <settings>
  <querystring>ReportId%3D954%26sqlid%3D779%26transformfile%3D0%26GroupID%3D106%26param1%3D36%26param2%3D0%26param3%3D0%26param4%3D0%26param5%3D0%26param6%3D0</querystring>
  <groupid>106</groupid>
  </settings>
  </selectrows>



Here is my modified XSLT:
  <?xml version="1.0" encoding="UTF-8" ?>
- <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:include href="http://nhc-ola/OnlineApplications/library/xslt/ReportTemplates.xslt" />
- <xsl:template match="/">
- <html>
- <table class="report" border="1">
  <xsl:apply-templates select="selectrows" />
  </table>
  </html>
  </xsl:template>
- <xsl:template match="selectrows">
- <THEAD>
- <TR class="header">
- <xsl:for-each select="selectrow[1]/*[not(name()='recordnumber')]">
- <xsl:choose>
  <xsl:when test="name(.) = 'doc_title'" />
  <xsl:when test="name(.) = 'report_title'" />
  <xsl:when test="name(.) = 'param_1'" />
  <xsl:when test="name(.) = 'param_2'" />
  <xsl:when test="name(.) = 'dt_groupid'" />
  <xsl:when test="name(.) = 'column_colour'" />
- <xsl:when test="name(.) = 'edit_form'">
- <TD align="left">
- <xsl:call-template name="headerise">
  <xsl:with-param name="inStr" select="name(.)" />
  </xsl:call-template>
  </TD>
  </xsl:when>
  <xsl:when test="name(.) = 'form_edit_rowid'" />
  <xsl:when test="name(.) = 'form_title'" />
  <xsl:when test="name(.) = 'form_edit_mode'" />
  <xsl:when test="name(.) = 'form_set_values'" />
  <xsl:when test="name(.) = 'new_line'" />
- <xsl:otherwise>
- <TD align="left">
- <xsl:call-template name="headerise">
  <xsl:with-param name="inStr" select="name(.)" />
  </xsl:call-template>
  </TD>
  </xsl:otherwise>
  </xsl:choose>
  </xsl:for-each>
  </TR>
  </THEAD>
  <xsl:apply-templates select="selectrow" />
- <!--  Put  any footer information here <TD> and </TD>
  -->
- <TR class="footer">
  <TD colspan="2">Total number of rows returned</TD>
  <TD />
- <TD class="int">
  <xsl:value-of select="count(//selectrow)" />
  </TD>
  </TR>
  </xsl:template>
- <!--  Template to pull the data out of the XML and format it as a report
  -->
- <xsl:template match="selectrow">
- <xsl:for-each select="new_line">
- <xsl:choose>
- <!--  if only one new_line
  -->
- <xsl:when test="not(preceding-sibling::new_line) and not(following-sibling::new_line)">
- <tr>
  ONE 1
  <xsl:apply-templates select="preceding-sibling::*" />
  </tr>
- <tr>
  ONE 2
  <xsl:apply-templates select="following-sibling::*" />
  </tr>
  </xsl:when>
- <!--  if the first newline
  -->
- <xsl:when test="not(preceding-sibling::new_line)">
- <tr>
  <xsl:apply-templates select="preceding-sibling::*" />
  </tr>
  </xsl:when>
- <!--  if the last new_line
  -->
- <xsl:when test="not(following-sibling::new_line)">
  <xsl:variable name="prevNodePos" select="count(preceding-sibling::new_line[1]/preceding-sibling::*)" />
  <xsl:variable name="thisNodePos" select="count(preceding-sibling::*)" />
- <tr>
  <xsl:apply-templates select="preceding-sibling::*[count(preceding-sibling::*) > $prevNodePos]" />
  </tr>
- <tr>
  <xsl:apply-templates select="following-sibling::*" />
  </tr>
  </xsl:when>
- <!--  all other new_line
  -->
- <xsl:otherwise>
  <xsl:variable name="prevNodePos" select="count(preceding-sibling::new_line[1]/preceding-sibling::*)" />
  <xsl:variable name="thisNodePos" select="count(preceding-sibling::*)" />
- <tr>
  <xsl:apply-templates select="preceding-sibling::*[count(preceding-sibling::*) > $prevNodePos]" />
  </tr>
  </xsl:otherwise>
  </xsl:choose>
  </xsl:for-each>
- <!--  My old way - how do I get the above to do this ?
  -->
- <xsl:for-each select="*[not(name()='recordnumber')]">
- <TD class="char">
  <xsl:value-of select="." />
  </TD>
  </xsl:for-each>
  </xsl:template>
  </xsl:stylesheet>


The very last for-each just pulls out all the nodes and formats them (as I do it at the moment - my actual stylesheet does <xsl:choose> here and formats the node diffrently depending upon the contents of the node).  How do I get the "new_line" test bits to "call" (if that is the correct word) a template for each node?

Thanks again.

Iain.

colly92002

ASKER
Oops sorry the indenting was lost above.  Here it is again a little clearer:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

  <xsl:include href="http://nhc-ola/OnlineApplications/library/xslt/ReportTemplates.xslt" />


    <xsl:template match="/">
        <html>

              <table class="report" border="1">
                  <xsl:apply-templates select="selectrows"/>
              </table>
        </html>
    </xsl:template>
    <xsl:template match="selectrows">
      <THEAD>
          <TR class="header">

            <xsl:for-each select="selectrow[1]/*[not(name()='recordnumber')]">
              <xsl:choose>
                <xsl:when test = "name(.) = 'doc_title'">
                </xsl:when>
                <xsl:when test = "name(.) = 'report_title'">
                </xsl:when>
                <xsl:when test = "name(.) = 'param_1'">
                </xsl:when>
                <xsl:when test = "name(.) = 'param_2'">
                </xsl:when>
                <xsl:when test = "name(.) = 'dt_groupid'">
                </xsl:when>
                <xsl:when test = "name(.) = 'column_colour'">
                </xsl:when>
                <xsl:when test = "name(.) = 'edit_form'">
                  <TD align="left">

                  <xsl:call-template name="headerise">
                    <xsl:with-param name="inStr" select="name(.)"/>
                  </xsl:call-template>
               
                 </TD>
                </xsl:when>
                <xsl:when test = "name(.) = 'form_edit_rowid'">
                </xsl:when>
                <xsl:when test = "name(.) = 'form_title'">
                </xsl:when>
                <xsl:when test = "name(.) = 'form_edit_mode'">
                </xsl:when>
                <xsl:when test = "name(.) = 'form_set_values'">
                </xsl:when>
                <xsl:when test = "name(.) = 'new_line'">
                </xsl:when>
                <xsl:otherwise>
                  <TD align="left">

                  <xsl:call-template name="headerise">
                    <xsl:with-param name="inStr" select="name(.)"/>
                  </xsl:call-template>
               
                 </TD>
                </xsl:otherwise>
              </xsl:choose>
            </xsl:for-each>
        </TR>
      </THEAD>
      <xsl:apply-templates select="selectrow"/>

        <!-- Put  any footer information here <TD> and </TD> -->
        <TR class="footer">
          <TD colspan="2">Total number of rows returned</TD>
          <TD></TD>
          <TD class="int"><xsl:value-of select="count(//selectrow)" /></TD>
        </TR>

</xsl:template>

    <!-- Template to pull the data out of the XML and format it as a report -->
    <xsl:template match="selectrow">
            <xsl:for-each select="new_line">
                <xsl:choose>
                    <!-- if only one new_line -->
                    <xsl:when test="not(preceding-sibling::new_line) and not(following-sibling::new_line)">
                        <tr>ONE 1 <xsl:apply-templates select="preceding-sibling::*"/></tr>
                        <tr>ONE 2 <xsl:apply-templates select="following-sibling::*"/></tr>
                    </xsl:when>
                    <!-- if the first newline -->
                    <xsl:when test="not(preceding-sibling::new_line)">
                        <tr><xsl:apply-templates select="preceding-sibling::*"/></tr>
                    </xsl:when>
                    <!-- if the last new_line -->
                    <xsl:when test="not(following-sibling::new_line) ">
                        <xsl:variable name="prevNodePos" select="count(preceding-sibling::new_line[1]/preceding-sibling::*)"/>
                        <xsl:variable name="thisNodePos" select="count(preceding-sibling::*)"/>
                        <tr><xsl:apply-templates select="preceding-sibling::*[count(preceding-sibling::*) &gt; $prevNodePos]"/></tr>
                        <tr><xsl:apply-templates select="following-sibling::*"/></tr>
                    </xsl:when>
                    <!-- all other new_line -->
                    <xsl:otherwise>
                        <xsl:variable name="prevNodePos" select="count(preceding-sibling::new_line[1]/preceding-sibling::*)"/>
                        <xsl:variable name="thisNodePos" select="count(preceding-sibling::*)"/>
                         <tr><xsl:apply-templates select="preceding-sibling::*[count(preceding-sibling::*) &gt; $prevNodePos]"/></tr>
                    </xsl:otherwise>
                </xsl:choose>
               
            </xsl:for-each>

          <!-- My old way - how do I get the above to do this ?-->
            <xsl:for-each select="*[not(name()='recordnumber')]">
            <TD class="char"><xsl:value-of select="."/></TD>
            </xsl:for-each>
            
    </xsl:template>



</xsl:stylesheet>

Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.
colly92002

ASKER
To make it clearer what I am trying to do:

Assuming there is only one "new_line", then I tried to do this:
                    <xsl:when test="not(preceding-sibling::new_line) and not(following-sibling::new_line)">
             <xsl:call-template name="show_row" >
                           <xsl:with-param name="row_data" select="preceding-sibling::*" />
                       </xsl:call-template>
             <xsl:call-template name="show_row" >
                 <xsl:with-param name="row_data" select="following-sibling::*" />
                        </xsl:call-template>
                    </xsl:when>

<!--END SNIPPET -->


Unfortunately, I am not sure how to get the "show_row" template to access the current node list.  I tried to pass it as a parameter but then I don't know how to access it, and then I thought maybe the current node list will be accessible, but unfortunately I cant get the following to work:

 <xsl:template name="show_row">
           <xsl:param name="row_data"/>
          
          <!-- My old way - how do I get the above to do this ?
            <xsl:for-each select="*[not(name()='recordnumber')]">
                -->
        <TR>
            <xsl:for-each select="*[not(name()='recordnumber')]">
            <TD class="char"><xsl:value-of select="."/></TD>
            </xsl:for-each>
          </TR>
    </xsl:template>
Gertone (Geert Bormans)

Hang on,
in your example I always see the same pattern
so maybe we are just making it too complex

The first three items form the first row,
then there is a new_line element
and then you have the next thre items for the second row
and this in every <selectrow> element

is this pattern always there
If it is we are making it too complex,
I would then recommend to hard code the sequence in your XSLT
Gertone (Geert Bormans)

colly92002,

having said that,
all you need is this

    <xsl:template match="selectrow/*">
        <xsl:if test="not(name()='recordnumber')">
            <TD class="char"><xsl:value-of select="."/></TD>
        </xsl:if>
   </xsl:template>

and get rid of the similar for-each in your stylesheet

cheers

Geert
I started with Experts Exchange in 2004 and it's been a mainstay of my professional computing life since. It helped me launch a career as a programmer / Oracle data analyst
William Peck
colly92002

ASKER
Thank you!  The mists are clearing.

here is my complete template:
 <xsl:template match="selectrow/*">
      
          <xsl:for-each select=".">
               
             <xsl:choose>
               <xsl:when test = "name(.) = 'doc_href'">
                 <td class="url">
                    <xsl:call-template name="linkurl">
                       <xsl:with-param name="target_url" select="."/> <!-- this is the content of the current node, with name doc_ref -->
                       <xsl:with-param name="title_url" select="following-sibling::doc_title"/> <!-- path depends on relative position of doc_title to doc_ref -->
                  </xsl:call-template>

                </td>  
               </xsl:when>
                <xsl:when test = "name(.) = 'doc_title'">
                </xsl:when>

<!--            Drill through reports -->
                <xsl:when test = "name(.) = 'drill_through'">
                 <td class="url">
                    <xsl:call-template name="linkreport">


                       <xsl:with-param name="target_id" select="."/> <!-- Replace 923 with the number of the report to call -->
                       <xsl:with-param name="param_val_1" select="../param_1"/> <!-- this is the content of the current node (i.e. the contents of the column called "drill_through" in the SQL).  In this example this is sent to the drill through report as parameter 1 -->
                       <xsl:with-param name="param_val_2" select="../param_2"/> <!-- Send the second parameter from the SQL-->
                       <xsl:with-param name="dt_groupid" select="../dt_groupid"/> <!-- Send the groupid from the SQL -->

                       <xsl:with-param name="title_url" select="following-sibling::report_title"/> <!-- The contents of the SQL column "report_title" contains the text that is displayed in the link.   -->
                  </xsl:call-template>

                </td>  
               </xsl:when>
                <!-- NODES TO IGNORE -->
                <xsl:when test = "name(.) = 'report_title'">
                </xsl:when>
                <xsl:when test = "name(.) = 'param_1'">
                </xsl:when>
                <xsl:when test = "name(.) = 'param_2'">
                </xsl:when>
                <xsl:when test = "name(.) = 'dt_groupid'">
                </xsl:when>
                <xsl:when test = "name(.) = 'column_colour'">
                </xsl:when>
                <xsl:when test = "name(.) = 'form_edit_rowid'">
                </xsl:when>
                <xsl:when test = "name(.) = 'form_title'">
                </xsl:when>
                <xsl:when test = "name(.) = 'form_edit_mode'">
                </xsl:when>
                <xsl:when test = "name(.) = 'form_set_values'">
                </xsl:when>      
                <xsl:when test = "name(.) = 'recordnumber'">
                </xsl:when>
                <xsl:when test = "name(.) = 'new_line'">
                  <!--Need to break up the line -->

                  <!--IGNORE-->

                </xsl:when>
                <!--CALL AN ONLINE FORM -->
                <xsl:when test = "name(.) = 'edit_form'">
                  <td class="url">
                    <xsl:call-template name="link_to_form">


                       <xsl:with-param name="target_id" select="."/> <!-- Can hard code this to the number of the forml -->
                       <xsl:with-param name="edit_rowid" select="../form_edit_rowid"/> <!-- if required this should be set to the column from the SQL that contains the unique identifier in the editable table  -->
                      <xsl:with-param name="edit_mode" select="../form_edit_mode"/> <!-- If known can be hard coded to "add" or "edit" -->

                       <xsl:with-param name="param_val_2" select="../param_2"/> <!-- Send the second parameter to the form (not used yet)-->

                      <xsl:with-param name="set_values" select="../form_set_values"/> <!-- List of value pairs (separated with semi-colons) that the form will populate -->

                       <xsl:with-param name="dt_groupid" select="../dt_groupid"/> <!-- Send the groupid from the SQL (if required)-->

                       <xsl:with-param name="form_title" select="following-sibling::form_title"/> <!-- The contents of the SQL column "form_title" contains the text that is displayed in the link.   -->
                  </xsl:call-template>

                </td>  
              </xsl:when>

<!--         Normal DATA row from SQL -->
              <xsl:otherwise>
               <td class="char">
                 <!--COLUMN COLOUR -->
                 <xsl:variable name="col_name"  select="name(preceding-sibling::*[1])"/>
                 <xsl:variable name="col_value"  select="(preceding-sibling::*[1])"/>
                 <xsl:choose>
                   <xsl:when test = "$col_name = 'column_colour'">
                     <xsl:attribute name='style'>
                       color=<xsl:value-of select="$col_value" />
                     </xsl:attribute>
                   </xsl:when>
                 </xsl:choose>
                  <!--DISPLAY THE VALUE -->
                  <xsl:value-of select="."/>
                </td>  
              </xsl:otherwise>
            </xsl:choose>
       
           </xsl:for-each>      


<!--  Basic example
 <xsl:if test="not(name()='recordnumber')">
            <TD class="char"><xsl:value-of select="."/></TD>
        </xsl:if>
-->
   </xsl:template>




I still do a for-each because I want to do something different depending upon the name of the node, which I hope is acceptable practice?
Perhaps it would be better to add an attribute to the node and process it depending upon that?  Although that is a whole new question!

Thanks again.

Iain.
ASKER CERTIFIED SOLUTION
Gertone (Geert Bormans)

Log in or sign up to see answer
Become an EE member today7-DAY FREE TRIAL
Members can start a 7-Day Free trial then enjoy unlimited access to the platform
Sign up - Free for 7 days
or
Learn why we charge membership fees
We get it - no one likes a content blocker. Take one extra minute and find out why we block content.
Not exactly the question you had in mind?
Sign up for an EE membership and get your own personalized solution. With an EE membership, you can ask unlimited troubleshooting, research, or opinion questions.
ask a question
colly92002

ASKER
Ah that seems so obvious now you have explained it!  I will will find the time to re-code the stylesheet in your suggested manner.

Thank you again for your time, as ever you have been most helpful.

Iain.
Gertone (Geert Bormans)

you are welcome, Iain
Get an unlimited membership to EE for less than $4 a week.
Unlimited question asking, solutions, articles and more.