Inside Xslt

Internet Explorer and Dynamic Styles

To indicate how much control you can exercise over XSLT transformations using the MSXML processor, Ill let the user dynamically sort the Planets table in this example. You can see the general idea in Figure 10.3. All the user has to do is to click a button to resort the table by name , mass, radius, or day.

Figure 10.3. Supporting dynamic XSLT transformations.

To sort the table, I use a stylesheet that puts the <xsl:sort> element to work, as youd expect. Heres how I perform the default sort, by planet name:

Listing 10.2 Applying Dynamic XSLT Transformations

<?xml version="1.0" encoding="iso-8859-1" ?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.1"> <xsl:template match="/"> <HTML> <HEAD> <TITLE> The Sorted Planets Table </TITLE> </HEAD> <BODY> <H1> The Sorted Planets Table </H1> <TABLE BORDER="2"> <TR> <TD>Name</TD> <TD>Mass</TD> <TD>Radius</TD> <TD>Day</TD> </TR> <xsl:apply-templates select="/PLANETS/PLANET"> <xsl:sort select="NAME" order="ascending"/> </xsl:apply-templates> </TABLE> </BODY> </HTML> </xsl:template> <xsl:template match="PLANET"> <TR> <TD><xsl:value-of select="NAME"/></TD> <TD><xsl:value-of select="MASS"/></TD> <TD><xsl:value-of select="RADIUS"/></TD> <TD><xsl:value-of select="DAY"/></TD> </TR> </xsl:template> </xsl:stylesheet>

This stylesheet sorts the planets in ascending alphabetic order, based on their names . Thats fine if thats all you want to do, but its possible to do more: you can let the user sort on other criteria as well. To do that, select the <xsl:sort> elements select attribute in this stylesheet and use JavaScript to change it dynamically from NAME to MASS, RADIUS, or whatever the user has asked for, then perform the XSLT transformation again. When the transformation is performed again, the new table appears, showing the new sort order.

As the first step in writing the HTML page that makes this possible, I load in planets.xml and the stylesheet I want to use, and perform the default sort, which is by planet name:

<HTML> <HEAD> <TITLE> Applying Dynamic Styles </TITLE> <SCRIPT LANGUAGE="JavaScript"> var XMLDocument; var XSLDocument; var HTMLtarget; function initialize() { XMLDocument = new ActiveXObject('MSXML2.DOMDocument.3.0'); XSLDocument = new ActiveXObject('MSXML2.DOMDocument.3.0'); HTMLtarget = document.all['targetDIV']; XMLDocument.validateOnParse = true; XMLDocument.load('planets.xml'); if (XMLDocument.parseError.errorCode != 0) { HTMLtarget.innerHTML = "Error!" return false; } XSLDocument.validateOnParse = true; XSLDocument.load('planets.xsl'); if (XSLDocument.parseError.errorCode != 0) { HTMLtarget.innerHTML = "Error!" return false; } HTMLtarget.innerHTML = XMLDocument.transformNode(XSLDocument); } . . .

Youve seen this part before. However, the user can also click a button to sort by mass, radius, and so on. To sort the table again, I write a function named sort and pass to it the name of the node (such as MASS) to sort on when the user clicks a button. This is how the various buttons you saw in Figure 10.3 are created:

<INPUT TYPE="BUTTON" ONCLICK="sort('NAME')" VALUE="Sort by name"></INPUT> <INPUT TYPE="BUTTON" ONCLICK="sort('MASS')" VALUE="Sort by mass"></INPUT> <INPUT TYPE="BUTTON" ONCLICK="sort('RADIUS')" VALUE="Sort by radius"></INPUT> <INPUT TYPE="BUTTON" ONCLICK="sort('DAY')" VALUE="Sort by day"></INPUT>

In the sort function, then, I want to perform a new sort, based on the name of the node passed to us. To perform the new sort, I change the select attribute of the <xsl:sort> element to the new node name to sort on. This is what the select attribute looks like now:

<xsl:apply-templates select="/PLANETS/PLANET"> <xsl:sort select="NAME" order="ascending"/> </xsl:apply-templates>

I can access that node in the stylesheet, now stored in the XSLDocument object, by passing an XPath expression to that objects selectSingleNode method. The selectSingleNode method returns a node object, and I can change the text value of that node with the node objects nodeValue property. In this case, I just set the select attribute to the new node name to sort on:

<HTML> <HEAD> <TITLE> Applying Dynamic Styles </TITLE> <SCRIPT LANGUAGE="JavaScript"> . . . function initialize() { . . . } function sort(sortNode) { (XSLDocument.selectSingleNode("//xsl:sort/@select")).nodeValue = sortNode; . . . } </SCRIPT> . . .

Now all thats left to do is to perform the transformation again, and display the results:

</HEAD> <HTML> <HEAD> <TITLE> Applying Dynamic Styles </TITLE> <SCRIPT LANGUAGE="JavaScript"> . . . function initialize() { . . . } function sort(sortNode) { (XSLDocument.selectSingleNode("//xsl:sort/@select")).nodeValue = sortNode; HTMLtarget.innerHTML = XMLDocument.transformNode(XSLDocument); } </SCRIPT> </HEAD>

And thats all it takes. You can see the results in Figure 10.3. When the user clicks a button, the table is sorted again on the node value he or she has selected (bear in mind that this is an alphabetic sort; to sort by numeric values, see the discussion of <xsl:sort> in Chapter 5) and the table is re-displayed in the new sort order. Heres the whole HTML page:

Listing 10.3 Applying Dynamic XSLT Transformations

<HTML> <HEAD> <TITLE> Applying Dynamic Styles </TITLE> <SCRIPT LANGUAGE="JavaScript"> var XMLDocument; var XSLDocument; var HTMLtarget; function initialize() { XMLDocument = new ActiveXObject('MSXML2.DOMDocument.3.0'); XSLDocument = new ActiveXObject('MSXML2.DOMDocument.3.0'); HTMLtarget = document.all['targetDIV']; XMLDocument.validateOnParse = true; XMLDocument.load('planets.xml'); if (XMLDocument.parseError.errorCode != 0) { HTMLtarget.innerHTML = "Error!" return false; } XSLDocument.validateOnParse = true; XSLDocument.load('planets.xsl'); if (XSLDocument.parseError.errorCode != 0) { HTMLtarget.innerHTML = "Error!" return false; } HTMLtarget.innerHTML = XMLDocument.transformNode(XSLDocument); } function sort(sortNode) { (XSLDocument.selectSingleNode("//xsl:sort/@select")).nodeValue = sortNode; HTMLtarget.innerHTML = XMLDocument.transformNode(XSLDocument); } </SCRIPT> </HEAD> <BODY ONLOAD="initialize()"> <CENTER> <DIV ID="targetDIV"></div> <BR> <BR> <INPUT TYPE="BUTTON" ONCLICK="sort('NAME')" VALUE="Sort by name"></INPUT> <INPUT TYPE="BUTTON" ONCLICK="sort('MASS')" VALUE="Sort by mass"></INPUT> <INPUT TYPE="BUTTON" ONCLICK="sort('RADIUS')" VALUE="Sort by radius"></INPUT> <INPUT TYPE="BUTTON" ONCLICK="sort('DAY')" VALUE="Sort by day"></INPUT> </CENTER> </BODY> </HTML>

In fact, theres more than one way to load XML and XSL documents into objects in the Internet Explorer. Ive been using the ActiveXObject class to create the XMLDocument and XSLDocument objects, but you could also create them directly, referring to the ActiveX objects that host XML documents by class ID as stored in the Windows Registry. In the following example I do that to load planets.xml and planets.xsl into XMLDocument and XSLDocument :

XMLDocument = document.all['XMLdoc']; XSLDocument = document.all['XSLdoc']; XMLDocument.load('planets.xml'); XSLDocument.load('planets.xsl'); . . . <OBJECT ID="XMLdoc" WIDTH="0" HEIGHT="0" CLASSID="clsid:f5078f32-c551-11d3-89b9-0000f81fe221"> </OBJECT> <OBJECT ID="XSLdoc" WIDTH="0" HEIGHT="0" CLASSID="clsid:f5078f32-c551-11d3-89b9-0000f81fe221"> </OBJECT>

This technique is not as robust as using the ActiveXObject class, because the class IDs may vary with Internet Explorer version. (These class IDs are for Internet Explorer 5.5.) However, theres another way to load XML and XSL documents into the Internet Explorer as well: you can use XML islands .

Категории