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
attachShadow
»switches of the light«, so contained elements in the light DOM become invisible? Is there a JS way to test if a given Element is visible for this scenario? – Dybbuk