AntonOfTheWoods
AntonOfTheWoods

Reputation: 953

xhtml vs html5 native web component behaviour

I have exactly the same html:

<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
  <meta charset="UTF-8"/>
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <title>Document</title>
</head>
<body>
<hello-world></hello-world>
<script>
class HelloWorld extends HTMLElement {
    constructor() {
      super();
      this.attachShadow({mode: 'open'});
    }
    connectedCallback() {
      this.shadowRoot.innerHTML = `<p>hello world</p>`;
    }
}
customElements.define('hello-world', HelloWorld)
</script>
</body>
</html>

Which works as I would have expected in both FF and Chrome if the file is served as Content-Type text/html; charset=UTF-8. If it is served as Content-Type application/xhtml+xml then they are both empty. I appear to get the same behaviour with or without the shadowDom.

If I have

this.shadowRoot.innerHTML = `hello world`;

Then I do get hello world. I get exactly the same behaviour on both FF and Chrome, so it looks like it's working as expected - where is this documented?

Is it because the browsers DOMs APIs are stopping being html5 DOMs and becoming XML DOMs, meaning that it is not longer possible to assign to innerHTML, and requires using tools like DOMParser, et al.?

Upvotes: 1

Views: 145

Answers (1)

Alohci
Alohci

Reputation: 82986

This is to do with the way the XML parser works. The <p>...</p> tags are parsed as an element by the XML parser and not as text as the HTML parser would do, so the p element and its contents are never part of the string input to .innerHTML.

If you extract the JavaScript to an external file, and reference that instead, then it works fine because the JavaScript is then not being handed to the HTML/XML parser.

Alternatively, you can escape the < characters. So

<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
  <meta charset="UTF-8"/>
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <title>Document</title>
</head>
<body>
<hello-world></hello-world>
<script>
class HelloWorld extends HTMLElement {
    constructor() {
      super();
      this.attachShadow({mode: 'open'});
    }
    connectedCallback() {
      this.shadowRoot.innerHTML = `&lt;p>hello world&lt;/p>`;
    }
}
customElements.define('hello-world', HelloWorld)
</script>
</body>
</html>

The opposite compatibility applies though. It will work as desired in the application/xhtml+xml content type, but will interpret the tags as text in the text/html content type.

If you really need bi-glot behaviour and everything in the HTML file, you can use the "to end-of-line-commented CDATA hack". i.e.

<!DOCTYPE HTML>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
  <meta charset="UTF-8"/>
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <title>Document</title>
</head>
<body>
<hello-world></hello-world>
<script>
//<![CDATA[
class HelloWorld extends HTMLElement {
    constructor() {
      super();
      this.attachShadow({mode: 'open'});
    }
    connectedCallback() {
      this.shadowRoot.innerHTML = `<p>hello world</p>`;
    }
}
customElements.define('hello-world', HelloWorld)
//]]>
</script>
</body>
</html>

However, bi-glot HTML really isn't a thing any more. Just go with the content type you prefer.

Upvotes: 1

Related Questions