My understanding of web components was that you can use it to prevent css leaks from the web component to the parent. I need exactly that but for some reason styling inside the web component affects the whole document.
My current pairing buddy (ChatGPT) also doesn't understand what is happening 😅.
<template id="test-shadow-dom-template">
<slot name="menu-bar"></slot>
<span id="contents">
<slot>The content</slot>
</span>
</template>
<script type="text/javascript">
if(!customElements.get('test-shadow-dom')) {
customElements.define('test-shadow-dom', class extends HTMLElement {
constructor() {
super();
let template = document.getElementById("test-shadow-dom-template");
let templateContent = template.content;
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.appendChild(templateContent.cloneNode(true));
}
});
}
</script>
<div id="test-shadow-dom-container" class="box" style="height: 100%">
<test-shadow-dom id="my-id">
<style>
* {
font-style: italic;
}
</style>
<h1>Here is the actual contents...</h1>
</test-shadow-dom>
</div>
My understanding is that only the contents of the test-shadow-dom
would be italic yet everything on the webpage is italic.
Any idea what is going on?
See my long explanation on ::slotted
Slotted content remains in lightDOM, it is reflected to shadowDOM <slot>
it is NOT moved to shadowDOM <slot>
.
Thus your <style>
styles the global document, just like it would when placed inside a <div>
Note inheritable (global) styles (see long ::slotted post) DO style shadowDOM.
font-style
IS an inheritable style.
You can put the <style>
inside the <template>
; I omitted it for brievity.
Also note a slotted <style>
does NOT style the shadowDOM it is in (red border)
<div class="foo">This is my-element:</div>
<my-element>
<style>
* { /* in lightDOM, styles all of global DOM */
font-style: italic;
}
</style>
<style>
div {
border: 2px solid red;
}
</style>
my-element
</my-element>
<script>
customElements.define("my-element", class extends HTMLElement {
constructor() {
super()
.attachShadow({ mode: "open" })
.innerHTML = `<style>
* { /* in shadowDOM, styles only shadowDOM */
color:green;
font-weight: bold;
}
</style>
<div>Hello!</div>
<slot></slot>`;
}
})
</script>
You do have control of what users put in lightDOM, but only after all those elements are parsed (in lightDOM) can you access those elements from the connectedCallback
.
Provided that lightDOM isn't huge (like say 500+ elements) a basic setTimeout
will be enough to delay and execute code after parsing:
connectedCallback() {
setTimeout( () => {
console.log( this.innerHTML );
});
}