Last active
June 17, 2023 04:37
-
-
Save dfkaye/eaff068b3c82a3294bf5ab19de199822 to your computer and use it in GitHub Desktop.
XSLT in the browser using JavaScript, basic examples
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 3 August 2022 | |
| // XSLT in the browser using JavaScript, | |
| // basic examples | |
| // advanced examples to do: | |
| // 1. transform html fragments | |
| // 2. transform big fragments | |
| // 3. transform templates (big sort) | |
| // This has been around for 20 years, yet almost no one knows how to do this in the | |
| // browser. XML and XSL sources are taken from w3schools at | |
| // https://www.w3schools.com/xml/xsl_client.asp | |
| // 1. You must process anything in the main document thread. The XSLTProcessor is not | |
| // available in Workers, e.g. | |
| // 2. You can fetch an XML or XSL document over the network | |
| // 2.1. Using XMLHttpRequest and use the response object's `responseXML` field. | |
| // 2.2. Using fetch() and returning the response.text(), then processing the text | |
| // strings with DOMParser, described in bullet no. 3. | |
| // 3. You can define XML and XSL strings in the browser - as we'll do here - and then | |
| // pass them through a DOMParser's parseFromString() method to create XML and XSL | |
| // *documents*. | |
| // 4. Use an XSLTProcessor to | |
| // 4.1. Load the XSL document by the importStylesheet(xsl) method, | |
| // 4.2. Transform XML | |
| // 4.2.1. Use the transformToFragment(xml, document) to create a document fragment | |
| // 4.2.2. Use the transformToDocument(xml, document) to create a new XML document. | |
| var xml =`<?xml version="1.0" encoding="UTF-8"?> | |
| <catalog> | |
| <cd> | |
| <title>Empire Burlesque</title> | |
| <artist>Bob Dylan</artist> | |
| <country>USA</country> | |
| <company>Columbia</company> | |
| <price>10.90</price> | |
| <year>1985</year> | |
| </cd> | |
| <cd> | |
| <title>Hide your heart</title> | |
| <artist>Bonnie Tyler</artist> | |
| <country>UK</country> | |
| <company>CBS Records</company> | |
| <price>9.90</price> | |
| <year>1988</year> | |
| </cd> | |
| <cd> | |
| <title>Greatest Hits</title> | |
| <artist>Dolly Parton</artist> | |
| <country>USA</country> | |
| <company>RCA</company> | |
| <price>9.90</price> | |
| <year>1982</year> | |
| </cd> | |
| <cd> | |
| <title>Still got the blues</title> | |
| <artist>Gary Moore</artist> | |
| <country>UK</country> | |
| <company>Virgin records</company> | |
| <price>10.20</price> | |
| <year>1990</year> | |
| </cd> | |
| <cd> | |
| <title>Eros</title> | |
| <artist>Eros Ramazzotti</artist> | |
| <country>EU</country> | |
| <company>BMG</company> | |
| <price>9.90</price> | |
| <year>1997</year> | |
| </cd> | |
| <cd> | |
| <title>One night only</title> | |
| <artist>Bee Gees</artist> | |
| <country>UK</country> | |
| <company>Polydor</company> | |
| <price>10.90</price> | |
| <year>1998</year> | |
| </cd> | |
| <cd> | |
| <title>Sylvias Mother</title> | |
| <artist>Dr.Hook</artist> | |
| <country>UK</country> | |
| <company>CBS</company> | |
| <price>8.10</price> | |
| <year>1973</year> | |
| </cd> | |
| <cd> | |
| <title>Maggie May</title> | |
| <artist>Rod Stewart</artist> | |
| <country>UK</country> | |
| <company>Pickwick</company> | |
| <price>8.50</price> | |
| <year>1990</year> | |
| </cd> | |
| <cd> | |
| <title>Romanza</title> | |
| <artist>Andrea Bocelli</artist> | |
| <country>EU</country> | |
| <company>Polydor</company> | |
| <price>10.80</price> | |
| <year>1996</year> | |
| </cd> | |
| <cd> | |
| <title>When a man loves a woman</title> | |
| <artist>Percy Sledge</artist> | |
| <country>USA</country> | |
| <company>Atlantic</company> | |
| <price>8.70</price> | |
| <year>1987</year> | |
| </cd> | |
| <cd> | |
| <title>Black angel</title> | |
| <artist>Savage Rose</artist> | |
| <country>EU</country> | |
| <company>Mega</company> | |
| <price>10.90</price> | |
| <year>1995</year> | |
| </cd> | |
| <cd> | |
| <title>1999 Grammy Nominees</title> | |
| <artist>Many</artist> | |
| <country>USA</country> | |
| <company>Grammy</company> | |
| <price>10.20</price> | |
| <year>1999</year> | |
| </cd> | |
| <cd> | |
| <title>For the good times</title> | |
| <artist>Kenny Rogers</artist> | |
| <country>UK</country> | |
| <company>Mucik Master</company> | |
| <price>8.70</price> | |
| <year>1995</year> | |
| </cd> | |
| <cd> | |
| <title>Big Willie style</title> | |
| <artist>Will Smith</artist> | |
| <country>USA</country> | |
| <company>Columbia</company> | |
| <price>9.90</price> | |
| <year>1997</year> | |
| </cd> | |
| <cd> | |
| <title>Tupelo Honey</title> | |
| <artist>Van Morrison</artist> | |
| <country>UK</country> | |
| <company>Polydor</company> | |
| <price>8.20</price> | |
| <year>1971</year> | |
| </cd> | |
| <cd> | |
| <title>Soulsville</title> | |
| <artist>Jorn Hoel</artist> | |
| <country>Norway</country> | |
| <company>WEA</company> | |
| <price>7.90</price> | |
| <year>1996</year> | |
| </cd> | |
| <cd> | |
| <title>The very best of</title> | |
| <artist>Cat Stevens</artist> | |
| <country>UK</country> | |
| <company>Island</company> | |
| <price>8.90</price> | |
| <year>1990</year> | |
| </cd> | |
| <cd> | |
| <title>Stop</title> | |
| <artist>Sam Brown</artist> | |
| <country>UK</country> | |
| <company>A and M</company> | |
| <price>8.90</price> | |
| <year>1988</year> | |
| </cd> | |
| <cd> | |
| <title>Bridge of Spies</title> | |
| <artist>T\`Pau</artist> | |
| <country>UK</country> | |
| <company>Siren</company> | |
| <price>7.90</price> | |
| <year>1987</year> | |
| </cd> | |
| <cd> | |
| <title>Private Dancer</title> | |
| <artist>Tina Turner</artist> | |
| <country>UK</country> | |
| <company>Capitol</company> | |
| <price>8.90</price> | |
| <year>1983</year> | |
| </cd> | |
| <cd> | |
| <title>Midt om natten</title> | |
| <artist>Kim Larsen</artist> | |
| <country>EU</country> | |
| <company>Medley</company> | |
| <price>7.80</price> | |
| <year>1983</year> | |
| </cd> | |
| <cd> | |
| <title>Pavarotti Gala Concert</title> | |
| <artist>Luciano Pavarotti</artist> | |
| <country>UK</country> | |
| <company>DECCA</company> | |
| <price>9.90</price> | |
| <year>1991</year> | |
| </cd> | |
| <cd> | |
| <title>The dock of the bay</title> | |
| <artist>Otis Redding</artist> | |
| <country>USA</country> | |
| <company>Stax Records</company> | |
| <price>7.90</price> | |
| <year>1968</year> | |
| </cd> | |
| <cd> | |
| <title>Picture book</title> | |
| <artist>Simply Red</artist> | |
| <country>EU</country> | |
| <company>Elektra</company> | |
| <price>7.20</price> | |
| <year>1985</year> | |
| </cd> | |
| <cd> | |
| <title>Red</title> | |
| <artist>The Communards</artist> | |
| <country>UK</country> | |
| <company>London</company> | |
| <price>7.80</price> | |
| <year>1987</year> | |
| </cd> | |
| <cd> | |
| <title>Unchain my heart</title> | |
| <artist>Joe Cocker</artist> | |
| <country>USA</country> | |
| <company>EMI</company> | |
| <price>8.20</price> | |
| <year>1987</year> | |
| </cd> | |
| </catalog> | |
| `; | |
| var xsl = `<?xml version="1.0" encoding="UTF-8"?> | |
| <xsl:stylesheet version="1.0" | |
| xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> | |
| <xsl:template match="/"> | |
| <h2>My CD Collection</h2> | |
| <table border="1"> | |
| <tr bgcolor="#9acd32"> | |
| <th style="text-align:left">Title</th> | |
| <th style="text-align:left">Artist</th> | |
| </tr> | |
| <xsl:for-each select="catalog/cd"> | |
| <tr> | |
| <td><xsl:value-of select="title" /></td> | |
| <td><xsl:value-of select="artist" /></td> | |
| </tr> | |
| </xsl:for-each> | |
| </table> | |
| </xsl:template> | |
| </xsl:stylesheet> | |
| `; | |
| var parser = new DOMParser; | |
| var xmldoc = parser.parseFromString(xml, "text/xml"); | |
| var xsldoc = parser.parseFromString(xsl, "text/xml"); | |
| var processor = new XSLTProcessor | |
| processor.importStylesheet(xsldoc); | |
| // either an XML fragment | |
| var tfrag = processor.transformToFragment(xmldoc, document); | |
| console.warn( tfrag ); | |
| while (document.body.firstChild) { | |
| document.body.firstChild.remove(); | |
| } | |
| document.body.appendChild(tfrag); | |
| // or a new XML document | |
| var tdoc = processor.transformToDocument(xmldoc, document); | |
| console.log( tdoc ); | |
| /********************************************************************/ | |
| // We can simulate network fetching of either document using blob URLs. | |
| // Note that the fetch() promise chain calls the DOMParser API in this | |
| // example (taken from https://codetogo.io/how-to-fetch-xml-in-javascript/). | |
| var source = xml; | |
| var blob = new Blob([source], { type: "text/plain" }); | |
| var url = URL.createObjectURL(blob); | |
| fetch(url) | |
| .then(response => response.text()) | |
| .then(data => { | |
| var parser = new DOMParser(); | |
| var xmldoc = parser.parseFromString(data, "application/xml"); | |
| console.log(xml); | |
| var tfrag = processor.transformToFragment(xmldoc, document); | |
| console.warn( tfrag.querySelector("h2") ); | |
| console.warn( tfrag.querySelector("table") ); | |
| /*while (document.body.firstChild) { | |
| document.body.firstChild.remove(); | |
| }*/ | |
| document.body.appendChild(tfrag); | |
| }) | |
| .catch(console.error); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 5 August 2022 | |
| // sorting and subsorting | |
| // https://www.xml.com/pub/a/2002/07/03/transform.html | |
| var xml = `<?xml version="1.0" encoding="UTF-8"?> | |
| <ul data="catalog"> | |
| <li data="cd"><title>one</title><artist>first</artist></li> | |
| <li data="cd"><title>two</title><artist>second</artist></li> | |
| <li data="cd"><title>three</title><artist>third</artist></li> | |
| <li data="cd"><title>four</title><artist>fourth</artist></li> | |
| <li data="cd"><title>five</title><artist>fifth</artist></li> | |
| <li data="cd"><title>six</title><artist>first</artist></li> | |
| <li data="cd"><title>seven</title><artist>second</artist></li> | |
| <li data="cd"><title>eight</title><artist>third</artist></li> | |
| <li data="cd"><title>nine</title><artist>fourth</artist></li> | |
| <li data="cd"><title>ten</title><artist>fifth</artist></li> | |
| </ul> | |
| `; | |
| var xsl = `<?xml version="1.0" encoding="UTF-8"?> | |
| <xsl:stylesheet version="1.0" | |
| xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> | |
| <xsl:template match="/"> | |
| <h2>My CD Collection</h2> | |
| <table border="1"> | |
| <tr bgcolor="#9acd32"> | |
| <th style="text-align:left">Artist</th> | |
| <th style="text-align:left">Title</th> | |
| </tr> | |
| <xsl:for-each select='*[@data="catalog"]/*[@data="cd"]'> | |
| <xsl:sort select="artist"/> | |
| <xsl:sort select="title"/> | |
| <tr> | |
| <td><xsl:value-of select="artist" /></td> | |
| <td><xsl:value-of select="title" /></td> | |
| </tr> | |
| </xsl:for-each> | |
| </table> | |
| </xsl:template> | |
| </xsl:stylesheet> | |
| `; | |
| var parser = new DOMParser; | |
| var xmldoc = parser.parseFromString(xml, "text/xml"); | |
| var xsldoc = parser.parseFromString(xsl, "text/xml"); | |
| var processor = new XSLTProcessor | |
| processor.importStylesheet(xsldoc); | |
| // either an XML fragment | |
| var tfrag = processor.transformToFragment(xmldoc, document); | |
| console.warn( tfrag ); | |
| while (document.body.firstChild) { | |
| document.body.firstChild.remove(); | |
| } | |
| document.body.appendChild(tfrag); | |
| // or a new XML document | |
| var tdoc = processor.transformToDocument(xmldoc, document); | |
| console.log( tdoc ); | |
| /********************************************************************/ | |
| // We can simulate network fetching of either document using blob URLs. | |
| // Note that the fetch() promise chain calls the DOMParser API in this | |
| // example (taken from https://codetogo.io/how-to-fetch-xml-in-javascript/). | |
| var source = xml; | |
| var blob = new Blob([source], { type: "text/plain" }); | |
| var url = URL.createObjectURL(blob); | |
| fetch(url) | |
| .then(response => response.text()) | |
| .then(data => { | |
| var parser = new DOMParser(); | |
| var xmldoc = parser.parseFromString(data, "application/xml"); | |
| console.log(xml); | |
| var tfrag = processor.transformToFragment(xmldoc, document); | |
| console.warn( tfrag.querySelector("h2") ); | |
| console.warn( tfrag.querySelector("table") ); | |
| /*while (document.body.firstChild) { | |
| document.body.firstChild.remove(); | |
| }*/ | |
| document.body.appendChild(tfrag); | |
| }) | |
| .catch(console.error); | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 5 August 2022 | |
| // xslt transform to json | |
| // 15 August 2022 | |
| // replace <pre><code>output</code></pre> | |
| // with <samp>output</samp> | |
| // and samp { /* css */ }. | |
| var xml = `<?xml version="1.0" encoding="UTF-8"?> | |
| <ul data="catalog"> | |
| <li data="cd"><title>one</title><artist>first</artist></li> | |
| <li data="cd"><title>two</title><artist>second</artist></li> | |
| <li data="cd"><title>three</title><artist>third</artist></li> | |
| <li data="cd"><title>four</title><artist>fourth</artist></li> | |
| <li data="cd"><title>five</title><artist>fifth</artist></li> | |
| <li data="cd"><title>six</title><artist>first</artist></li> | |
| <li data="cd"><title>seven</title><artist>second</artist></li> | |
| <li data="cd"><title>eight</title><artist>third</artist></li> | |
| <li data="cd"><title>nine</title><artist>fourth</artist></li> | |
| <li data="cd"><title>ten</title><artist>fifth</artist></li> | |
| </ul> | |
| `; | |
| var xsl = `<?xml version="1.0" encoding="UTF-8"?> | |
| <xsl:stylesheet version="1.0" | |
| xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> | |
| <xsl:output method="text" /> | |
| <xsl:template match="/">[ | |
| <xsl:for-each select='*[@data="catalog"]/*[@data="cd"]'> | |
| <xsl:sort select="artist"/> | |
| <xsl:sort select="title"/> | |
| { | |
| "artist": "<xsl:value-of select="artist" />", | |
| "title": "<xsl:value-of select="title" />" | |
| }, | |
| </xsl:for-each> | |
| {} | |
| ] | |
| </xsl:template> | |
| </xsl:stylesheet> | |
| `; | |
| var parser = new DOMParser; | |
| var xmldoc = parser.parseFromString(xml, "text/xml"); | |
| var xsldoc = parser.parseFromString(xsl, "text/xml"); | |
| var processor = new XSLTProcessor | |
| processor.importStylesheet(xsldoc); | |
| // either an XML fragment | |
| var tfrag = processor.transformToFragment(xmldoc, document); | |
| // or a new XML document | |
| var tdoc = processor.transformToDocument(xmldoc, document); | |
| console.dir( tdoc ); | |
| // write the fragment to the current document | |
| while (document.body.firstChild) { | |
| document.body.firstChild.remove(); | |
| } | |
| var style = document.createElement('style'); | |
| style.textContent = ` | |
| samp { | |
| background: lightcyan; | |
| color: purple; | |
| display: inline-block; | |
| margin-bottom: 1em; | |
| outline: 1px dashed darkcyan; | |
| white-space: pre-wrap; | |
| width: 100%; | |
| } | |
| `; | |
| document.body.appendChild(style); | |
| var samp = document.createElement('samp'); | |
| var data = JSON.parse(tfrag.firstChild.data.trim()); | |
| samp.textContent = JSON.stringify(data.filter(item => item.artist), null, 2); | |
| document.body.appendChild(samp); | |
| /********************************************************************/ | |
| // We can simulate network fetching of either document using blob URLs. | |
| // Note that the fetch() promise chain calls the DOMParser API in this | |
| // example (taken from https://codetogo.io/how-to-fetch-xml-in-javascript/). | |
| var source = xml; | |
| var blob = new Blob([source], { type: "text/plain" }); | |
| var url = URL.createObjectURL(blob); | |
| fetch(url) | |
| .then(response => response.text()) | |
| .then(data => { | |
| var parser = new DOMParser(); | |
| var xmldoc = parser.parseFromString(data, "application/xml"); | |
| // console.log(xmldoc); | |
| var tfrag = processor.transformToFragment(xmldoc, document); | |
| var data = JSON.parse(tfrag.firstChild.data.trim()); | |
| var samp = document.createElement('samp'); | |
| samp.textContent = JSON.stringify(data.filter(item => item.artist), null, 2); | |
| document.body.appendChild(samp); | |
| }) | |
| .catch(console.error); | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 16 June 2023 | |
| // document.implementation.createHTMLDocument | |
| var xslt = new XSLTProcessor | |
| var parser = new DOMParser; | |
| var xsl = parser.parseFromString( | |
| `<?xml version="1.0"?> | |
| <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> | |
| <xsl:output method="html" indent="yes" /> | |
| <xsl:template match="/"> | |
| <H>test</H> | |
| <xsl:apply-templates select="/b"> | |
| <b>updated</b> | |
| </xsl:apply-templates> | |
| </xsl:template> | |
| <xsl:template match="b"> | |
| <xsl:copy-of select="." /> | |
| </xsl:template> | |
| </xsl:stylesheet> | |
| `, "text/xml"); | |
| xslt.importStylesheet(xsl); | |
| var xml = document.implementation.createHTMLDocument("", "", null) | |
| var dom = document.implementation.createHTMLDocument("", "", null) | |
| var node = parser.parseFromString( | |
| `<b>original</b> | |
| `, "text/html").querySelector('b'); | |
| var clone = xml.importNode(node, true); | |
| xml.body.appendChild(clone); | |
| var fragment = xslt.transformToFragment(xml, dom); | |
| console.log(fragment.firstChild); | |
| var xsr = new XMLSerializer | |
| var str = xsr.serializeToString(fragment); | |
| str | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment