XSLT with Fetch() API — modern Javascript with/without Async and Await

Having looked at the use of XMLHttpRequest and jQuery $.ajax(), $.ajax() with Promises and $.get(), another modern way to load XML and XSLT files is to use the Javascript Fetch API. A further way to use Fetch is to incorporate Async and Await.

XSLT for the Modern Web
5 min readMay 3, 2024

This article follows on from my previous posts!

XSLT for the Modern Web — Re-visiting XSLT in the modern website landscape

Fetch() API and XSLTProcessor

The Javascript Fetch() API is seen as more modern replacement for XMLHttpRequest() and will work on pretty much everywhere except Internet Explorer — check on caniuse.com to verify which.
To use Fetch() we will still however need to serve our files from the server — see my post XSLT in the web browser — simple transformations on the server if you need to get setup.

Here is an example — developing from the examples in my previous posts — of using Fetch() to load XML and XSLT files. Fetch returns a Promise() with a .then() method upon completion of fetching the resource. A .catch() method is also available to catch errors.

In this example we need to use DOMParser to convert the response from fetch — which is provided as a text string — into XML fragment. (I’ll do a further article looking at DOMParser and XMLSerialiser).

To find out more about the Fetch() API and its methods, Mozilla Developer Networks has a good overview.

<!DOCTYPE html>
<html lang="en">
<head>
<meta encoding="utf-8">
<title>XSLT</title>
</head>
<body>

<div id="example"></div>

<script type="text/javascript">
(function(){

function fetchLoad(){

if(!('fetch' in window)) {
console.log('Fetch does not appear to be available in this browser. Please try another.');
return;
}

if(!('XSLTProcessor' in window)) {
console.log('XSLTProcessor does not appear to be available in this browser. Please try another.');
return;
}

if(!('DOMParser' in window)){
console.log('DOMParser does not appear to be available in this browser. Please try another.');
return;
}

const xsltProcessor = new XSLTProcessor();
const parser = new DOMParser();

//XSLT stylesheet
fetch('data/example.xsl').then(function(response){
// Do stuff with the response
if(response){
return response.text();
}
}).then(function(data) {
const xsl = parser.parseFromString(data, "application/xml");
xsltProcessor.importStylesheet(xsl);
})
.catch(function(error) {
console.log('Looks like there was a problem: ', error);
});

//XML file
fetch('data/example.xml').then(function(response){
// Do stuff with the response
if(response){
return response.text();
}
}).then(function(data) {
const xml = parser.parseFromString(data, "application/xml");
const fragment = xsltProcessor.transformToFragment(xml, document);
document.getElementById("example").appendChild(fragment);
})
.catch(function(error) {
console.log('Looks like there was a problem: ', error);
});
}
window.addEventListener("load", fetchLoad, false);
})();
</script>
</body>
</html>

Can we use Fetch() API with ‘await’ and ‘async’?

The Javascript keywords await and async are a relatively new feature to the evolving language and can allow synchronously-appearing functions to behave in an asynchronous manner. This can appear confusing at times!

The general usage is as follows, with the async keyword placed before the function name and the await placed inside the function itself. The function or process that is called after the await keyword is expected to take some time to complete — in this case it will be loading a file.

async myfunction function(){

//call a function that is likely to take time to complete fully
let data = await (function)
}

//myfunction behaves asynchronously
myfunction();

The keyword async, when used before a function, returns a Javascript Promise(). It does this even if the return within the function is not specified as a Promise().

Fetch() API with await and async

Here’s a detailed view on using the asynchronous function to load the XML and XSLT files.

To make this example even more concise I have used arrow functions here rather than the traditional function (plus omitted the .catch() for brevity).

loadFile("data/example.xsl").then(data => {
let xsl = parser.parseFromString(data, "application/xml");
xsltProcessor.importStylesheet(xsl);
});

loadFile("data/example.xml").then(data => {
const xml = parser.parseFromString(data, "application/xml");
const fragment = xsltProcessor.transformToFragment(xml, document);
document.getElementById("example").appendChild(fragment);
});

Now for the complete example:


<!DOCTYPE html>
<html lang="en">
<head>
<meta encoding="utf-8">
<title>XSLT</title>
</head>
<body>

<div id="example"></div>

<script type="text/javascript">
(function(){
function fetchLoad(){

if(!('fetch' in window)) {
console.log('Fetch does not appear to be available in this browser. Please try another.');
return;
}
if(!('XSLTProcessor' in window)) {
console.log('XSLTProcessor does not appear to be available in this browser. Please try another.');
return;
}
if(!('DOMParser' in window)){
console.log('DOMParser does not appear to be available in this browser. Please try another.');
return;
}

const xsltProcessor = new XSLTProcessor();
const parser = new DOMParser();

loadFile("data/example.xsl").then(data => {
let xsl = parser.parseFromString(data, "application/xml");
xsltProcessor.importStylesheet(xsl);
});

loadFile("data/example.xml").then(data => {
const xml = parser.parseFromString(data, "application/xml");
const fragment = xsltProcessor.transformToFragment(xml, document);
document.getElementById("example").appendChild(fragment);
});
}

//async function

async function loadFile(filepath){
const response = await fetch(filepath);
if(!response.ok){
console.log('Looks like there was a problem: ', response.status);
}
const text = await response.text();
return text;
}

window.addEventListener("load", fetchLoad, false);
})();
</script>
</body>
</html>

There is a slight problem here however — we can’t be sure on the order of the files being loaded.

This is a problem because we want:

xsltProcessor.importStylesheet(xsl);

to be set before…

const fragment = xsltProcessor.transformToFragment(xml, document);

We need the XSLT file loaded before the XML file — Javascript Promises() provide a .then() method we can use to call Javascript in a sequential order we need.

Fetch() API with await and async, using .then()

This example is very similar than before but the loading of the XML file is only initiated in the .then() of the Promise which returned from the loading of the XSLT file. This ensures that the XSLT file will always be initiated first.

A detailed view:

loadFile("data/example.xsl").then(data => {
const xsl = parser.parseFromString(data, "application/xml");
xsltProcessor.importStylesheet(xsl);
}).then(loadFile("data/example.xml").then(data => {
const xml = parser.parseFromString(data, "application/xml");
const fragment = xsltProcessor.transformToFragment(xml, document);
document.getElementById("example").appendChild(fragment);
}));

Now for the complete example:

<!DOCTYPE html>
<html lang="en">
<head>
<meta encoding="utf-8">
<title>XSLT</title>
</head>
<body>

<div id="example"></div>

<script type="text/javascript">
(function(){

function fetchLoad(){
if(!('fetch' in window)) {
console.log('Fetch does not appear to be available in this browser. Please try another.');
return;
}
if(!('XSLTProcessor' in window)) {
console.log('XSLTProcessor does not appear to be available in this browser. Please try another.');
return;
}
if(!('DOMParser' in window)){
console.log('DOMParser does not appear to be available in this browser. Please try another.');
return;
}

const xsltProcessor = new XSLTProcessor();
const parser = new DOMParser();

loadFile("data/example.xsl").then(data => {
const xsl = parser.parseFromString(data, "application/xml");
xsltProcessor.importStylesheet(xsl);
}).then(loadFile("data/example.xml").then(data => {
const xml = parser.parseFromString(data, "application/xml");
const fragment = xsltProcessor.transformToFragment(xml, document);
document.getElementById("example").appendChild(fragment);
}));
}

// async function

async function loadFile(filepath){
const response = await fetch(filepath);
if(!response.ok){
console.log('Looks like there was a problem: ', response.status);
}
const text = await response.text();
return text;
}

window.addEventListener("load", fetchLoad, false);
})();
</script>
</body>
</html>

If you’ve found this article helpful, here’s a list of the other articles on using XSLT in the Modern Web available — more to come soon!

--

--

XSLT for the Modern Web

Re-learning XSLT in the context of the Modern Web - occassional writer, reader, Digital Humanities enthusiast All text is 100% non-AI - learn, explore, reuse