Bug #5458
closedTrying to replace contents of HTML document's root element html with xsl:result-document href="?." method="ixsl:replace-content" creates or leaves duplicated/empty head/body elements
100%
Description
I have tried to see whether Saxon-JS 2.3 allows to replace the contents of the html
root element of an HTML DOM document loaded in the browser, using e.g. xsl:result-document href="?." method="ixsl:replace-content"
:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
xmlns:ixsl="http://saxonica.com/ns/interactiveXSLT"
xmlns:saxon="http://saxon.sf.net/"
expand-text="yes">
<xsl:param name="counter" as="xs:integer" select="1"/>
<xsl:template match="html">
<xsl:result-document href="?." method="ixsl:replace-content">
<xsl:apply-templates/>
</xsl:result-document>
</xsl:template>
<xsl:template match="title/text()">
<xsl:value-of select="replace(., '[0-9]+', string((//input[@id = 'button1'] => ixsl:get('dataset'))?counter + 1))"/>
</xsl:template>
<xsl:template match="input[@type = 'button']/@value">
<xsl:attribute name="{name()}" select="'test ' || (.. => ixsl:get('dataset'))?counter + 1"/>
</xsl:template>
<xsl:template match="input[@type = 'button']/@data-counter">
<xsl:attribute name="{name()}" select=". + 1"/>
</xsl:template>
<xsl:output method="html" html-version="5"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="input[@type = 'button' and @id = 'button1']" mode="ixsl:onclick">
<xsl:apply-templates select="/html"/>
</xsl:template>
<xsl:template match="/" name="xsl:initial-template">
<h1>Test</h1>
<p>Run with {system-property('xsl:product-name')} {system-property('xsl:product-version')} {system-property('Q{http://saxon.sf.net/}platform')} at {saxon:timestamp()}.</p>
<input type="button" id="button1" value="test {$counter}" data-counter="{$counter}"/>
</xsl:template>
</xsl:stylesheet>
but I notice, that somehow, the new contents I want to insert to replace the old, is added to the DOM tree, but after each replace there are empty head
and body
elements inserted or left. That way the whole approach doesn't work well as the duplicated head
and body
(those stray empty ones and the newly inserted ones) mess up the rendering of the page.
Example test page is at https://martin-honnen.github.io/xslt/2022/replaceHtmlElementContents1.html, it uses above stylesheet which on the initial load and run through the script inserts some button you can click to test the attempt to replace the contents of the html
element; if you open the browser console and the tree view you can see that after each click the html
element node has additional empty head
and body
elements before the head
and body
with the contents the stylesheet attempts to insert. Tests where done with Google Chrome.
Used files are at https://github.com/martin-honnen/martin-honnen.github.io/blob/master/xslt/2022/replaceHtmlElementContents1.html, https://github.com/martin-honnen/martin-honnen.github.io/blob/master/xslt/2022/replaceBodyContentsTest1.xsl, https://github.com/martin-honnen/martin-honnen.github.io/blob/master/xslt/2022/replaceBodyContentsTest1.xsl.sef.json
Updated by Martin Honnen over 2 years ago
I have now also tested with Firefox, it shows the same empty head
and body
elements showing up after each insert in the DOM tree. Somehow its rendering of the rest of the (newly inserted) contents isn't hampered by those elements, as it is in Google Chrome.
Updated by Norm Tovey-Walsh over 2 years ago
- Status changed from New to In Progress
- Assignee set to Norm Tovey-Walsh
Updated by Norm Tovey-Walsh over 2 years ago
My first thought was that it was some odd interaction between the replacement and the appendToBody
setting for destination
, but that doesn't seem to be it.
Updated by Norm Tovey-Walsh over 2 years ago
When you specify replace-content
, we use target.innerHTML=""
to clear the contents of the target before appending to it. It appears that html.innerHTML=""
magically adds an empty head
and an empty body
because we live in a universe devoid of sense and reason.
This doesn't happen if the elements are removed with removeChild
. We could
- Always use
removeChild
, but I don't know if that carries any sort of performance impact - Only use
removeChild
when the element ishtml
- Document that you can't replace the contents of the
html
element directly, you have to do the head and body separately.
I don't think choice 3 is very good.
Aaaand a few minutes of web searching suggest that removeChild
is faster.
So it's 1, I think.
Updated by Martin Honnen over 2 years ago
Using target.textContent = ""
is another option, it seems, in Chrome and Firefox it removes any child nodes but doesn't try to "fix" the document structure by adding empty head
and body
elements.
Updated by Norm Tovey-Walsh over 2 years ago
- Status changed from In Progress to Resolved
- Applies to JS Branch Trunk added
- Fix Committed on JS Branch 2, Trunk added
I replaced the innerHTML
approach with a simple removeChild
loop. New unit test added. (Thanks, Martin!)
Updated by Debbie Lockett over 2 years ago
- Status changed from Resolved to Closed
- % Done changed from 0 to 100
- Fixed in JS Release set to SaxonJS 2.4
Bug fix applied in the SaxonJS 2.4 maintenance release.
Please register to edit this issue
Also available in: Atom PDF Tracking page