1

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.

dacracot
  • 22,002
  • 26
  • 104
  • 152
  • 1
    Works in Firefox version 64, not in Chrome version 71, nor Safari version 12. – dacracot Jan 04 '19 at 01:26
  • Can confirm that in Chrome it doesn't work, but works in Firefox. Also tried to use `$x(xpath)` in Chrome - it doesn't see the appended node. – qwermike Jan 04 '19 at 08:59

1 Answers1

2

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);
dacracot
  • 22,002
  • 26
  • 104
  • 152
qwermike
  • 1,446
  • 2
  • 12
  • 24