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>
TEMPLATES
are 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 atemplate
in 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
FORM
can be the main container. No need for atemplate
container.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>.content
is moved to shadowDOM.To re-use (clone) the
<TEMPLATE>
the code must be:super(document.getElementById("FormTwo").content.cloneNode(true));
Why your
template.content
failedYour code failed because with
template
has no contentTEMPLATE
isn't a regular HTMLElement, you have to append to.content
will work
Most Web Component examples show:
innerHTML
sets.content
under the hoodWhich explains why instead of:
template.content.appendChild(form);
you can write:
template.innerHTML = form.outerHTML;