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 >
</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.
CodePudding user response:
You have a shadowDOM, so don't need an extra
divwhen you style the component with:hostyou can manually move ligthDOM elements to shadowDOM
super()sets AND returns the 'this' scopeattachShadowsets AND returnsthis.shadowRootappendwasn't available in Internet Explorer, ("oldfashioned" devs still useappendChild)
note it moves the DOM elements from lightDOM to shadowDOM. And if you move Web Components, theconnectedCallbackwill run again (but lightDOM will then be empty) So be aware what you do in theconnectedCallback
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>
