Consider this code:
//js class FooBar extends HTMLElement { constructor(){ super(); } } customElements.define('foo-bar', FooBar); <!-- html --> <foo-bar> <h1>Test</h1> </foo-bar>
This will show »Test« within the browser.
If the constructor is changed to:
constructor () { super(); this.shadow = this.attachShadow({ mode: 'open' }) }
The »Test« disappears, since there is a shadow root now.
If the constructor is furthermore changed to
constructor () { super(); this.shadow = this.attachShadow({ mode: 'open' }); this.shadow.appendChild(document.createElement('slot')); }
The »Test« appears again, since there is now a default slot for all child Nodes of <foo-bar>
But what happens to the child nodes if there is no <slot />
within the shadow root. They still appear within this.children
and its style.display
property remains ""
. So they are within the dom, but not rendered, even thou thr css tells the opposite? What exactly happens here?
Advertisement
Answer
The full detailed explanation is at: ::slotted CSS selector for nested children in shadowDOM slot
<foo-bar> <h1>Test</h1> </foo-bar>
H1 is lightDOM,
“added” to shadowDOM/root <SLOT> the content is reflected to shadowDOM, NOT moved!!!
H1 always remains in lightDOM :
invisible (in the page) in lightDOM for elements with shadowDOM/root,
visible (in the page) for Custom Elements without shadowDOM/root
unless you move it explicitly with
appendChild
(or any DOM move operation)
You say: So they are within the dom, but not rendered, even thou CSS tells the opposite?
No, they are rendered, just like any normal DOM element. Just not visible any more.
You can test by including a SCRIPT tag in lightDOM.. it will render and execute!
In code snippets below
You reference lightDOM with this.querySelector("span").innerHTML="weird";
But referencing shadowDOM with this.shadowRoot.querySelector("span").innerHTML="weird";
Does not work, because the DIV (with the SPAN inside) is black-boxed in a <SLOT>
<template id="MY-ELEMENT"> <style> :host { display: inline-block; font-family: Arial; } ::slotted(div){ color:blue; } ::slotted(span){ color:gold; /* alas, you can style the 'box', not elements inside */ } </style> <h3><slot></slot></h3> </template> <style> span { background:lightcoral; /* from global/host CSS, style slotted content lightDOM */ } </style> <script> customElements.define('my-element', class extends HTMLElement { constructor() { super().attachShadow({mode: 'open'}) .append(document.getElementById(this.nodeName).content.cloneNode(true)); } }); </script> <my-element> <div>Hello <span>Component</span> World!</div> </my-element>
Check the Component in F12 Dev Tools:
Chrome & Firefox:
The DIV is not in shadowDOM/root, remains invisible in lightDOM
all elements/styles will always reflect to shadowDOM/root
click ‘reveal’ takes you to the lightDOM
So to shadowDOM, slotted content is a black-box of elements & styles;
reflected from lightDOM
that is why::slotted
can only style the box, and not what is inside.
Note: edit that DIV in F12 Console, you will see changes immediately reflect to shadowDOM
SLOTs & lightDOM are LIVE connections
By changing <slot name=...>
you can make interactions (think Routes, Tabs, Answers) which previously needed lots more coding (remember those jQuery show/hide days?)
<template id="MY-ELEMENT"> Custom Element SLOTs are: <slot name=answer></slot> </template> <style> img { /* style all IMGs in lightDOM */ max-width: 100vw; max-height: 70vh; } </style> <script> customElements.define('my-element', class extends HTMLElement { connectedCallback() { this.attachShadow({mode: 'open'}) .append(document.getElementById(this.nodeName).content.cloneNode(true)); this.onclick = (evt) => { const answer = evt.composedPath()[0].innerText; // button label this.shadowRoot.querySelector('slot').name = answer; this.children[0].slot = answer;//include lightDOM buttons again } } }); </script> <my-element> <span slot=answer><button>Cool</button><button><b>Awesome</b></button><button>Great</button></span> <div slot=Cool><img src="https://i.imgur.com/VUOujQT.jpg"></div> <span slot=Awesome> <h3>SUPER!</h3></span> <div slot=Awesome><img src="https://i.imgur.com/y95Jq5x.jpg"></div> <div slot=Great><img src="https://i.imgur.com/gUFZNQH.jpg"></div> </my-element>
More SLOT related answers can be found with StackOverflow Search: Custom Elements SLOTs