expanding/collapsing nested nodes in xslt

2020-02-15 08:35发布

问题:

I have an xml in the following format;

<BasePeriod>
.
.
.
.
<Period>
    <start> stdate1 </start>
    <end> endate1 </end>
    <subperiod>
       <substart> substart1 </substart>
       <subend> subend1 </subend>
    </subperiod>
    <type> abc1 </type>
</Period>
<Period>
    <start> stdate1 </start>
    <end> endate1 </end>
    <subperiod>
       <substart> substart2 </substart>
       <subend> subend2 </subend>
    </subperiod>
    <type> abc2 </type>
</Period>
<Period>
    <start> stdate1 </start>
    <end> endate1 </end>
    <subperiod>
       <substart> substart3 </substart>
       <subend> subend3 </subend>
    </subperiod>
    <type> abc3 </type>
</Period>
</BasePeriod>

What I would like to do is - have the Period and SubPeriod collapsed when I open the html and then expand them when I click on it.

I have the following xslt:

<xsl:for-each select="SvcPeriods/BasePeriod">
    <tr>
        <td>
            <xsl:value-of select="startDate"/>
        </td>
        <td>
            <xsl:value-of select="endDate"/>
    </td>
    <td>
        <a href="javascript:expandIt(per_expand{position()}), period_expand{position()}" name="period_expand{position()}" class="expandit">Periods</a>
        <div id="per_expand{position()}" style="display:none;" >
            <table>
                <thead>
                <tr>
                    <th>
                        Start Date
                    </th>
                    <th>
                        End Date
                    </th>
                    <th>
                        Sub Periods
                    </th>
                    <th>
                        Type
                    </th>
                </tr>
                </thead>
                <xsl:for-each select="Period">
                    <tr>
                        <td>
                            <xsl:value-of select="start"/>
                        </td>
                        <td>
                            <xsl:value-of select="end"/>
                        </td>
                        <td>
                            <a href="javascript:expandIt(subper_expand{position()}), subperiod_expand{position()}" name="subperiod_expand{position()}" class="expandit">Sub Periods</a>
                            <div id="subper_expand{position()}" style="display:none;" >
                                <table>
                                    <tr>
                                        <th >
                                            Start Date
                                        </th>
                                        <th >
                                            End Date
                                        </th>
                                    </tr>
                                    <xsl:for-each select="subperiod">
                                        <tr>
                                            <td>
                                                <xsl:value-of select="substart"/>
                                            </td>
                                            <td>
                                                <xsl:value-of select="subend"/>
                                            </td>
                                        </tr>
                                    </xsl:for-each>
                                </table>
                            </div>
                        </td>
                        <td>
                            <xsl:value-of select="type"/>
                        </td>
                    </tr>
                </xsl:for-each>
            </table>
        </div>
    </td>
</tr>
</xsl:for-each>

<script language="JavaScript">
function expandIt(whichEl, link) {
    whichEl.style.display = (whichEl.style.display == "none" ) ? "" : "none";
    if ( link ) { 
         if ( link.innerHTML ) {
            if ( whichEl.style.display == "none" ) {
                  link.innerHTML = "[+] " + link.innerHTML.substring( 4 );
               } else {
                  link.innerHTML = "[-] " + link.innerHTML.substring( 4 );
               }
         }
     }
 }
 </script>

With the above, I'm not able to expand the sub period. I then tried passing document.getElementById(subper_expand{position()}') as the first argument to the expandit function. This time I'm able to expand the sub period, but the issue is, that it always refers to the sub period of the first period (even when I'm clicking inside the 2nd or 3rd period).

Can someone help me out here.

Thanks!

回答1:

Break your XSL into multiple templates

It's good practice to use many templates rather then one with "hidden" <xsl:for-each> elements.

BasePeriod:

<xsl:template match="SvcPeriods/BasePeriod">
    <tr>
        <td>
            <xsl:value-of select="startDate"/>
        </td>
        <td>
            <xsl:value-of select="endDate"/>
        </td>
        <td>
            <a href="javascript:expandIt(per_expand{position()},
                per_expand{position()})"
                name="period_expand{position()}" class="expandit">Periods</a>
            <div id="per_expand{position()}" style="display:none;">
                <table>
                    <tr>
                        <th> Start Date </th>
                        <th> End Date </th>
                        <th> Sub Periods </th>
                        <th> Type </th>
                    </tr>
                    <xsl:apply-templates select="Period"/>
                </table>
            </div>
        </td>
    </tr>

    <xsl:call-template name="expandIt"/>
</xsl:template>

Period:

<xsl:template match="Period">
    <tr>
        <td>
            <xsl:value-of select="start"/>
        </td>
        <td>
            <xsl:value-of select="end"/>
        </td>
        <td>
            <a href="javascript:expandIt(subper_expand{position()},
                subperiod_expand{position()})"
                name="subperiod_expand{position()}" 
                class="expandit">Sub Periods</a> 
            <div id="subper_expand{position()}" style="display:none;">
                <table>
                    <tr>
                        <th> Start Date </th>
                        <th> End Date </th>
                    </tr>
                    <xsl:apply-templates select="subperiod"/>
                </table>
            </div>
        </td>
        <td>
            <xsl:value-of select="type"/>
        </td>
    </tr>
</xsl:template>

subperiod:

<xsl:template match="subperiod">
    <tr>
        <td>
            <xsl:value-of select="substart"/>
        </td>
        <td>
            <xsl:value-of select="subend"/>
        </td>
    </tr>
</xsl:template>

expandIt:

<xsl:template name="expandIt">
    <script language="JavaScript">
    function expandIt(whichEl, link) {
        whichEl.style.display = (whichEl.style.display == "none" ) ? "" : "none";
        if ( link ) { 
             if ( link.innerHTML ) {
                if ( whichEl.style.display == "none" ) {
                      link.innerHTML = "[+] " + link.innerHTML.substring( 4 );
                   } else {
                      link.innerHTML = "[-] " + link.innerHTML.substring( 4 );
                   }
             }
         }
     }
    </script>       
</xsl:template>

As you see I changed:

<a href="javascript:expandIt(subper_expand{position()}),
    subperiod_expand{position()}"

to:

<a href="javascript:expandIt(subper_expand{position()},
    subperiod_expand{position()})"

(same for per_expand). Result (using Opera):

Next (clicking Periods link):

Next (clicking sub periods):

It seems to be ok, expanding and collapsing work as expected.