Reputation:
class MyElement extends HTMLElement {
constructor() {
super()
const shadow = this.attachShadow({mode: 'open'})
shadow.appendChild(document.createTextNode('Yadda blah'))
const span = document.createElement('span')
span.appendChild(document.createTextNode('Can I style U?'))
shadow.appendChild(span)
}
}
customElements.define('my-element', MyElement)
my-element {
border: 1px solid black;
}
span {
font-weight: bold;
}
<my-element></my-element>
As you can see, my-element
is styled, however, the span
used within my-element
is not. Not even saying my-element span {font-weight: bold;}
in the stylesheet makes any styles effective.
Is there any way to apply styles to this span
?
Upvotes: 5
Views: 480
Reputation: 4577
You have to apply the style to the shadow DOM. A common but soon-to-be-outdated technique is to add a <style>
element to the shadow DOM and set its text contents as the CSS you want to apply within the custom element.
The snippet below does just that. The style is saved to a variable, but you can place it anywhere as long you're conscious of how often its context is run. Following Intervalia's advice, it's best to apply a style to the custom element using :host
.
It's also possible to add a <link>
element instead, but that may cause the element to pop-in without a style (FOUC)
const myElementStyle = `
:host {
border: 1px solid black;
}
span {
font-weight: bold;
}
`
class MyElement extends HTMLElement {
constructor() {
super()
const shadow = this.attachShadow({mode: 'open'});
const style = document.createElement('style');
style.textContent = myElementStyle;
shadow.appendChild(style);
shadow.appendChild(document.createTextNode('Yadda blah'));
const span = document.createElement('span');
span.appendChild(document.createTextNode('Can I style U?'));
shadow.appendChild(span);
}
}
customElements.define('my-element', MyElement)
<my-element></my-element>
Another option is to use Constructable Stylesheets, which should soon be available in modern browsers. These let you create a stylesheet object that can embed styles or import styles from external resources.
I wrote up an answer here, so I'll just post a snippet that answers your question:
const customSheet = new CSSStyleSheet();
customSheet.replaceSync(`
:host {
border: 1px solid black;
}
span {
font-weight: bold;
}
`);
class MyElement extends HTMLElement {
constructor() {
super()
const shadow = this.attachShadow({
mode: 'open'
})
shadow.adoptedStyleSheets = [customSheet];
shadow.appendChild(document.createTextNode('Yadda blah'))
const span = document.createElement('span')
span.appendChild(document.createTextNode('Can I style U?'))
shadow.appendChild(span)
}
}
customElements.define('my-element', MyElement)
<my-element></my-element>
Upvotes: 2
Reputation: 6948
That is the expected behavior of shadow dom. Since your HTML looks like <my-element></my-element>
, you can target my-element
from your css, but the span
is not part of the actual dom, so you can't target it.
You can only apply styles to the span
from within the shadow dom context.
There are better ways of defining css in this case, but just to make a point, the following apply styles to the span
, but not to my-element
:
class MyElement extends HTMLElement {
constructor() {
super()
const shadow = this.attachShadow({mode: 'open'})
shadow.appendChild(document.createTextNode('Yadda blah'))
const span = document.createElement('span')
span.appendChild(document.createTextNode('Can I style U?'))
shadow.appendChild(span)
const styleTag = document.createElement('style')
styleTag.innerHTML = `
my-element {
border: 1px solid black;
}
span {
font-weight: bold;
}
`
shadow.appendChild(styleTag)
}
}
customElements.define('my-element', MyElement)
<my-element></my-element>
Upvotes: 1