I'm injecting an XML string generated from a web service, then trying to use XPath to query the attribute values using the following code.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>tox-js</title>
</head>
<body>
<script>
//
// -----------------------------------------------
// tox element
class Tox extends HTMLElement
{
constructor(url)
{
super();
fetch(url)
.then((response)=>
{
console.log("status: "+response.status);
return response.text();
})
.then((text)=>
{
console.log("text: "+text);
try
{
var dp = new DOMParser();
var xmlDOM = dp.parseFromString(text, "text/xml");
this.appendChild(xmlDOM.documentElement);
return true;
}
catch(err)
{
console.log("err: "+err.message);
return false;
}
})
.then((ok)=>
{
if (ok)
{
try
{
var xpe = new XPathEvaluator();
var txt = xpe.evaluate("//tox-js/example/@timestamp",document,null,XPathResult.STRING_TYPE,null);
console.log("//tox-js/example/@timestamp: "+txt.stringValue);
txt = xpe.evaluate("//tox-js/example/@feedback",document,null,XPathResult.STRING_TYPE,null);
console.log("//tox-js/example/@feedback: "+txt.stringValue);
}
catch(err)
{
console.log("err: "+err.message);
}
}
else
console.log("not ok");
}
);
}
}
//
// -----------------------------------------------
// register our element with the DOM
customElements.define('tox-js',Tox);
//
// -----------------------------------------------
// create an instance and add it to the body
document.body.appendChild(new Tox('http://localhost:8080/tox/example.test.formatted?in_mask=YYYYMMDD'));
// -----------------------------------------------
//
</script>
</body>
</html>
The result has the custom element injected.
<html lang="en">
<head>...</head>
<body>
<script>...</script>
<tox-js>
<example timestamp="20180103142036" feedback="ok">20190103</example>
</tox-js>
</body>
<html>
The console log confirms the return status and XML, but the result of the XPath is blank.
[Log] status: 200 (toxElement3.html, line 20)
[Log] text: <example timestamp="20190103142036" feedback="ok">20190103</example> (toxElement3.html, line 25)
[Log] //tox-js/example/@timestamp: (toxElement3.html, line 47)
[Log] //tox-js/example/@feedback: (toxElement3.html, line 49)
Where have I gone wrong? This should not be a timing issue since I'm using .then
to wait for the previous step.
Seems it is related to the namespaces. The following XPath works for me:
//tox-js/*[local-name()='example']/@timestamp
Check this answer: XPath Doesn't Work in Dynamic HTML-Document
Also you can use document.createElement()
or insertAdjacentHTML()
to create element from text as described here: Creating a new DOM element from an HTML string using built-in DOM methods or Prototype
In this case your XPath will work as expected.
<html lang="en">
<head></head>
<body>
<script>
window.addEventListener('load', () => {
var text = `<example timestamp="20180103142036" feedback="ok">20190103</example>`;
var el = document.getElementsByTagName('tox-js')[0];
el.insertAdjacentHTML('afterbegin', text);
var xpe = new XPathEvaluator();
var txt = xpe.evaluate("//tox-js/example/@timestamp",document,null,XPathResult.STRING_TYPE,null);
console.log(`//tox-js/example/@timestamp: ${txt.stringValue}`);
});
</script>
<tox-js>
</tox-js>
</body>
<html>
P.S. I can't explain why the problem happens when using DOMParser
. Maybe there are different namespaces for document
and DOMParser
. So if somebody has more details, feel free to extend the answer.
From the provided example...
var dp = new DOMParser();
var xmlDOM = dp.parseFromString(text, "text/xml");
this.appendChild(xmlDOM.documentElement);
...becomes...
this.insertAdjacentHTML('afterbegin', text);