I am working with XSLT and need to pass an XSLT variable to a JavaScript function to dynamically update the content on the page. Specifically, I have a variable in my XSLT template that I want to use in a JavaScript function to modify an HTML element.
Here is my XSLT template:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:outline="http://wkhtmltopdf.org/outline"
xmlns="http://www.w3.org/1999/xhtml">
<xsl:output doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
indent="yes" />
<xsl:template match="outline:outline">
<html>
<head>
<title>TOC</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<!-- Note: I want to dynamically update the following h1 by JS -->
<h1 id="toc">Table of Contents</h1>
<ul id="top">
<xsl:apply-templates select="outline:item/outline:item"/>
</ul>
<script type="text/javascript">
const languageMap = {
'ENG': 'Table of Contents',
'JPN': '目次',
'CHS': '目录',
'CHT': '目錄'
};
<!-- Note: the following function gets the languageTitle from XSLT and replaces h1 with matched value -->
function updateTOCHeader(languageTitle) {
const tocHeader = document.getElementById('toc');
tocHeader.style.color = "green";
if(languageMap[languageTitle]) {
tocHeader.innerText = languageMap[languageTitle];
}
}
</script>
</body>
</html>
</xsl:template>
<xsl:template match="outline:item">
<xsl:choose>
<!-- Note: the following if test checks if the ancestor item has a title starting with 'language_title:' -->
<xsl:when test="ancestor::outline:item[starts-with(@title, 'language_title:')]">
<xsl:variable name="language_title" select="normalize-space(substring-after(ancestor::outline:item[@title and starts-with(@title, 'language_title:')]/@title, 'language_title:'))"/>
<script type="text/javascript">
<!-- Note: call the following function to update the TOC header based on language title -->
updateTOCHeader("<xsl:value-of select="$language_title"/>");
</script>
</xsl:when>
<xsl:otherwise>
<li>
<xsl:if test="@title != ''">
<div class="{if (outline:item) then 'category-item' else ''}">
<span class="toc-link">
<a>
<xsl:if test="@link">
<xsl:attribute name="href"><xsl:value-of select="@link"/></xsl:attribute>
</xsl:if>
<xsl:value-of select="@title"/>
</a>
</span>
<span class="page-no">
<xsl:value-of select="@page"/>
</span>
</div>
</xsl:if>
<ul>
<xsl:apply-templates select="outline:item"/>
</ul>
</li>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Here is sample XML:
<?xml version="1.0" encoding="UTF-8"?>
<outline xmlns="http://wkhtmltopdf.org/outline">
<item title="" page="0"/>
<!-- Note: I want to pass the substring of the item title (e.g. 'JPN') to JS -->
<item title="language_title:JPN" page="1">
<item title="Table of Contents" page="2"/>
</item>
<item title="document" page="2">
<item title="introduction" page="1">
<item title="Chapter1" page="2">
<item title="Section1" page="3"/>
</item>
<item title="Chapter2" page="4">
<item title="Section2" page="5"/>
</item>
</item>
</item>
<item title="" page="20"/>
</outline>
I think your current code tries to call the function before it is defined, change the code to e.g.
<script type="text/javascript">
<!-- Note: call the following function to update the TOC header based on language title -->
document.addEventListener('DOMContentLoaded', function(evt) { updateTOCHeader("<xsl:value-of select="$language_title"/>"); });
</script>
that way the function is defined first and is called later once a DOM has been loaded.
A different approach would be to ensure the block declaring the map and the function is included before there is an attempt to call the function e.g. by putting the
<script type="text/javascript">
const languageMap = {
'ENG': 'Table of Contents',
'JPN': '目次',
'CHS': '目录',
'CHT': '目錄'
};
<!-- Note: the following function gets the languageTitle from XSLT and replaces h1 with matched value -->
function updateTOCHeader(languageTitle) {
const tocHeader = document.getElementById('toc');
tocHeader.style.color = "green";
if(languageMap[languageTitle]) {
tocHeader.innerText = languageMap[languageTitle];
}
}
</script>
into the head
section of the HTML you want to create.