Stack Overflow Asked by Mendy on August 15, 2020
The CSS ::slotted
selector selects children of the <slot>
element.
however, when trying to select grandchildren like with ::slotted(*)
, ::slotted(*) *
, or ::slotted(* *)
, the selector doesn’t seem to take effect.
class MyElement extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({mode: 'open'})
shadowRoot.innerHTML = `
<style>
::slotted(*) {
display: block;
border: solid blue 1px;
padding: 3px;
}
::slotted(*) span {
display: block;
border: solid red 1px;
padding: 3px;
}
::slotted(* span) {
display: block;
border: solid green 1px;
padding: 3px;
}
</style>
<slot></slot>
`;
}
}
customElements.define('my-element', MyElement);
<my-element>
<p>
<span>Test</span>
</p>
</my-element>
Note how the span doesn’t get the border.
Is this expected behavior? I wasn’t able to find concrete documentation for this.
If yes, is there a way to work around this?
TL;DR
slotted content remains in lightDOM, is reflected to a <slot>
::slotted(*)
can only target the lightDOM SKIN with simple selectors
Yes, ::slotted()
not styling nested elements is expected behavior.
The term slotted
is counterintuitive,
it implies element lightDOM is moved to shadowDOM
slotted lightDOM is NOT moved, it remains.. hidden.. in lightDOM
the content is reflected to a<slot></slot>
Or from Google Developer Documentation
Conceptually, distributed nodes can seem a bit bizarre.
Slots don't physically move DOM; they render it at another location inside the shadow DOM.
I use the term reflected instead of render because render implies you can access it in shadowDOM. You can not, because slotted content isn't in shadowDOM... only reflected from lightDOM.
The main takeway from the W3C standards discussions (@hayatoito here and here) is:
https://developer.mozilla.org/en-US/docs/Web/CSS/::slotted
From Mozilla developer Emilio:
source: https://github.com/w3c/webcomponents/issues/889
The performance issue is that it increments the amount of subtrees in which every node needs to go look for rules that affect to them.
Right now the logic goes like: if you're slotted, traverse your slots and collect rules in their shadow trees as needed. This is the code This is nice because the complexity of styling the element depends directly on the complexity of the shadow trees that you're building, and it only affects slotted nodes.
If you want to allow combinators past slotted then every node would need to look at its ancestor and prev-sibling chain and look at which ones of them are slotted, then do that process for all their slots. Then, on top, you also need to change the general selector-matching code so that selectors that do not contain slotted selectors don't match if you're not in the right shadow tree.
That's a cost that you pay for all elements, regardless of whether you use Shadow DOM or ::slotted, and is probably just not going to fly.
:slotted( S )
got limited CSS selector functionality:► it only takes simple selectors for S. --> Basically anything with a space won't work
► it only targets lightDOM 'skin'. --> In other words, only the first level
::slotted(h1)
and ::slotted(p)
works
::slotted(.foo)
works
::slotted(span)
(or anything deeper) will not work (not a 'skin' element)
<my-element>
<h1>Hello World</h1>
<p class=foo>
<span>....</span>
</p>
<p class=bar>
<span>....</span>
</p>
</my-element>
The <span>
is hidden in lightDOM, any changes made there will continue to reflect to its slotted representation.
That means you can apply any styling you want with CSS in the main DOM
(or a parent shadowDOM container if you wrapped <my-element>
in one)
<style>
my-element span {
.. any CSS you want
}
<style>
Note: ::slotted([Simple Selector])
confirms to Specificity rules,
but (being simple) does not add weight to lightDOM skin selectors, so never gets higher Specificity.
You might need !important
in some (rare) use cases.
<style>
::slotted(H1) {
color: blue !important;
}
<style>
If you move lightDOM with appendChild
or append
(or insertBefore or cloneNode, etc.) from lightDOM to shadowDOM, you can do all styling you want.
Without using <slot></slot>
and :slotted()
(maybe not call it a workaround) It is a different/powerful way of styling shadowDOM content:
Apple finally implemented this in Safari 13.1, March 2020
see:
Note! ::part
styles shadowDOM, <slot></slot>
content remains in lightDOM!
be aware: might contain v0 documentation!
https://developers.google.com/web/fundamentals/web-components/shadowdom?hl=en#composition_slot
https://polymer-library.polymer-project.org/2.0/docs/devguide/style-shadow-dom#style-your-elements
https://developer.mozilla.org/en-US/docs/Web/API/HTMLSlotElement/slotchange_event
::part() - https://developer.mozilla.org/en-US/docs/Web/CSS/::part
Related SO answers: WCSLOT
Change the slot-name on buttonclick and pull in new content from lightDOM:
<template id=MY-ELEMENT>
<style>
::slotted([slot="Awesome"]){
background:lightgreen
}
</style>
<slot><!-- all unslotted content goes here --></slot>
<slot id=answer name=unanswered></slot>
</template>
<style>
img { /* style all IMGs in lightDOM */
max-height: 200px;
border:4px solid green;
}
</style>
<my-element>
SLOTs are: <button>Cool</button> <button>Awesome</button> <button>Great</button>
<span slot=unanswered>?</span>
<div slot=Cool> <img src="https://i.imgur.com/VUOujQT.jpg"></div>
<span slot=Awesome><b>SUPER!</b></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>
<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 label = evt.composedPath()[0].innerText; // Cool,Awesome,Great
this.shadowRoot.getElementById("answer").name = label;
}
}
});
</script>
Correct answer by Danny '365CSI' Engelman on August 15, 2020
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP