I use C3 to render graphs in an HTML page that uses Saxon-JS and a (compiled, SEF) XSL stylesheet. C3 generates an SVG image (a bar chart), and provides a possibility to respond to click events on parts of the chart (e.g., a bar), with an onclick function.
c3.generate(
{ bindto: ...
, data:
{ type: 'bar'
, columns: c3_columns
, onclick: function(data, element) { ... }
}
...
})
I would like to process the click event in my XSL stylesheet. However:
The onclick function is not a JavaScript event handler in the usual sense. The data
parameter contains data but is not an Event
instance. Also, the JavaScript event
variable is undefined
in the body of the onclick function.
When I dispatch a custom event from the onclick function, I can not handle it in my XSL. That means that when I define <xsl:template match="*" mode="ixsl:onchartclick">
, this template is not called when I dispatch a chartclick
event from the onclick function.
Next, I tried to call a function defined in my XSL stylesheet:
<xsl:function xmlns:chart="chart" name="chart:click">
This is called from the onclick function (JavaScript) like
SaxonJS.XPath.evaluate("chart:click('hello chartCLick')", [],
{ namespaceContext: {'chart' : 'chart'}
});
Although the onclick function is called, the XSL function isn't; All I get in the browser console is "Unknown function Q{chart}click()".
This scenario is a bit complex, but I will include two simplified code samples that hopefully show what I am trying to do.
In the Javascript I have the onclick handler.
function chartClickHandler(data, element) {
// Show the contents of the data.
console.log(`Click on chart`);
Object.entries(data).forEach(([k, v]) => console.log(` ${k} : ${v}`));
// Send a custom event.
const event = new CustomEvent('chartclick', data);
document.getElementsByTagName('body')[0].dispatchEvent(event);
// Call a function in the XSL stylesheet.
SaxonJS.XPath.evaluate(`chart:click('hello chartCLick')`, [],
{ namespaceContext: {'chart' : 'chart'}
});
}
This is called from the HTML. For simplicity, I don't use C3, but:
<button onclick="chartClickHandler({x: 1, y: 2}, this)">test</button>
In the XSL stylesheet that is given to Saxon-JS I have:
<xsl:template match="body" mode="ixsl:onchartclick">
<xsl:if test="$show-debug-messages">
<xsl:message>
Chartclick event properties: <xsl:value-of select="ixsl:eval('Object.keys') => ixsl:apply([ixsl:event()])"/>
</xsl:message>
</xsl:if>
</xsl:template>
<xsl:function xmlns:chart="chart" name="chart:click">
<xsl:param name="data"/>
<xsl:message>
Chart click data: <xsl:value-of select="serialize($data)"/>
</xsl:message>
</xsl:function>
As said before, all I get is
Click on chart debugger eval code:3:11
x : 1 debugger eval code:4:52
y : 2 debugger eval code:4:52
Uncaught
Object { message: "Unknown function Q{chart}click()", stack: ...
This example still involves many parts, but the essence of my question is in the title: How to call a Saxon-JS function or template from JavaScript. Or how to intercept custom events.
As I said in a comment, I think to call a function in an SEF you should use SaxonJS.transform({ initialFunction : 'Q{namespace-uri}function-name', functionParams: [function arguments here], ..}, ..)
.
Example is e.g. https://martin-honnen.github.io/xslt/2022/xsltFunctionCallTest2.html
Content:
<html lang="en">
<head>
<title>Saxon-JS 2 test</title>
<script src="../../Saxon-JS-2.3/SaxonJS2.rt.js"></script>
<script>
var internalStylesheet;
document.addEventListener('DOMContentLoaded', e => {
var options = {
stylesheetLocation : 'xsltFunctionCallTest1.xsl.sef.json',
sourceLocation: 'sample1.xml',
destination: 'appendToBody'
};
SaxonJS.transform(options, 'async').then(result => { internalStylesheet = result.stylesheetInternal; console.log(internalStylesheet); console.log(result); });
});
</script>
<script>
function functionCallTest1() {
SaxonJS.transform({ destination: 'raw', stylesheetLocation : 'xsltFunctionCallTest1.xsl.sef.json', initialFunction: 'Q{http://example.com/mf}f1', functionParams: [ { x: 1, y: 'foo' }] }, 'async').then(result => console.log(result.principalResult));
}
</script>
</head>
<body>
<h1>Test</h1>
</body>
</html>
where the first transform generates e.g.
<section>
<h2>Test</h2>
<p>Run with {system-property('xsl:product-name')} {system-property('xsl:product-version')} {system-property('Q{http://saxon.sf.net/}platform')}</p>
<input type="button" value="test"
onclick="functionCallTest1();"/>
</section>
Caveat: in the XSLT, make sure you have visibility="public"
on the XSLT xsl:function
declared.