Skip to content

Instantly share code, notes, and snippets.

@dfkaye
Last active June 17, 2023 04:37
Show Gist options
  • Select an option

  • Save dfkaye/eaff068b3c82a3294bf5ab19de199822 to your computer and use it in GitHub Desktop.

Select an option

Save dfkaye/eaff068b3c82a3294bf5ab19de199822 to your computer and use it in GitHub Desktop.
XSLT in the browser using JavaScript, basic examples
// 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);
// 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);
// 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);
// 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