When I try to append template to the shadow DOM, it only shows as a "#documentFragment", and never renders or copies the actual elements structured within the template.
I spent hours trying to figure it out. The solution I found was to use:
- template.firstElementChild.cloneNode(true);
instead of:
- template.content.cloneNode(true);
then, and only then, everything works as expected.
My question is, am I doing something wrong?
const template = document.createElement('template');
const form = document.createElement('form');
const gateway = document.createElement('fieldset');
const legend = document.createElement('legend');
gateway.appendChild(legend);
const username = document.createElement('input');
username.setAttribute('type', 'email');
username.setAttribute('name', 'username');
username.setAttribute('placeholder', '[email protected]');
username.setAttribute('id', 'username');
gateway.appendChild(username);
const button = document.createElement('button');
button.setAttribute('type', 'button');
button.innerHTML = 'Next';
gateway.appendChild(button);
form.appendChild(gateway);
template.appendChild(form);
class UserAccount extends HTMLElement {
constructor() {
super();
const shadowDOM = this.attachShadow({
mode: 'open'
});
const clone = template.firstElementChild.cloneNode(true);
// This does not work
// const clone = template.content.cloneNode(true);
shadowDOM.appendChild(clone);
shadowDOM.querySelector('legend').innerHTML = this.getAttribute('api');
}
}
window.customElements.define('user-account', UserAccount);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<!-- <link rel="stylesheet" href="./css/main.css"> -->
<script src="./js/user-account.js" defer></script>
<title>Title</title>
</head>
<body>
<user-account api="/accounts"></user-account>
</body>
</html>
TEMPLATESare only interesting if you need to make multiple copies or want to work in plain HTML + CSS as much as possible.Many Web Components show the usage:
and then do:
Which, because the template is only used as a single "parent" container, you can write as:
Note:
super()returnsthis, andattachShadow()sets and returnsthis.shadowRoot... for freeTwo types of TEMPLATES
You can create a
<TEMPLATE>in DOM, or you can create atemplatein MemoryTemplates in Memory
9 out of 10 Memory-templates can be done with other HTMLElements as container,
as is the case with your code, where
FORMcan be the main container. No need for atemplatecontainer.If you do build a template in memory, learn the value of
append()over (the often misused)appendChild()In Memory templates are great for making (many) alterations (with code)
Templates in DOM
No need for trying to stuff HTML and CSS in JavaScript strings, you have a DOM in the HTML document!
Use the
<TEMPLATE>HTML Element.Add shadowDOM
<slot>to the mix and you will spent less time debugging JavaScript and more time writing semantic HTML.DOM Templates are great for easy HTML and CSS editting (in your IDE with syntax highlighting) of more static HTML/CSS structures
Here are both types of TEMPLATES with your code, which one is easier for a developer?
Note:
In the above code the
<TEMPLATE>.contentis moved to shadowDOM.To re-use (clone) the
<TEMPLATE>the code must be:super(document.getElementById("FormTwo").content.cloneNode(true));Why your
template.contentfailedYour code failed because with
templatehas no contentTEMPLATEisn't a regular HTMLElement, you have to append to.contentwill work
Most Web Component examples show:
innerHTMLsets.contentunder the hoodWhich explains why instead of:
template.content.appendChild(form);you can write:
template.innerHTML = form.outerHTML;