I'm trying to create a custom component which can take other HTML Elements, however, it has to be flexible by not having to use <template>
and slots templates in the HTML file every time the component is used. Already tried querying after every render by using const children = this.shadow.querySelectorAll('.menu > *')
within the connectedCallback()
function, but the result is an empty NodeList
class SideMenu extends HTMLElement {
constructor() {
super();
this.shadow = this.attachShadow({mode:'open'});
}
render() {
this.shadow.innerHTML = `
<style>
.menu {
display: flex;
flex-direction: column;
height: 100vh;
padding: 2em;
width: 33vw;
gap: 10px;
background-color: #2C2B2B;
}
</style>
<div class="menu">
</div>
`;
}
connectedCallback() {
this.render();
}
}
customElements.define('side-menu', SideMenu);
<side-menu>
<div>Element1</div>
<div>Element2</div>
<div>Element3</div>
<div>ElementN</div>
</side-menu>
Basically, the custom element has to be able to have any number of elements within, like an unordered list (<ul>
) which can have multiple <li>
inside.
You have a shadowDOM, so don't need an extra div
when you style the component with :host
you can manually move ligthDOM elements to shadowDOM
super()
sets AND returns the 'this' scope
attachShadow
sets AND returns this.shadowRoot
append
wasn't available in Internet Explorer, ("oldfashioned" devs still use appendChild
)
note it moves the DOM elements from lightDOM to shadowDOM. And if you move Web Components, the connectedCallback
will run again (but lightDOM will then be empty) So be aware what you do in the connectedCallback
customElements.define('side-menu', class extends HTMLElement {
constructor() {
super().attachShadow({mode:'open'}).innerHTML = `
<style>
:host {
display: flex; flex-direction: column; gap: 10px;
height: 100vh; width: 33vw;
padding: 2em;
background-color: #2C2B2B; color: gold;
}
</style>`;
}
connectedCallback() {
this.shadowRoot.append(...this.children);
}
});
<side-menu>
<div>Element1</div>
<div>Element2</div>
<div>Element3</div>
<div>ElementN</div>
</side-menu>