1

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>

Chrome Developer tools

Chrome developer tools

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?

rdehuyss
  • 734
  • 8
  • 14
  • You haven't created a web component with Javascript to use. – fat penguin Aug 24 '23 at 08:27
  • I'm afraid I don't understand your comment - the javascript for the custom element is also here in the SO Post? I also see the custom web component with it's shadow root in Chrome developer tools. – rdehuyss Aug 24 '23 at 08:33

2 Answers2

1

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 );
  });
}
Danny '365CSI' Engelman
  • 16,526
  • 2
  • 32
  • 49
  • Thanks fir this excellent explanation! Is there a way with web components where other developers will put style elements inside this web component to contain these styles so that the global document is not affected? – rdehuyss Aug 24 '23 at 10:36
  • 1
    Doable, put the `` – Danny '365CSI' Engelman Aug 24 '23 at 11:32
0

You should add styles inside template, not the component.

 <template id="test-shadow-dom-template">
   <style>
     * {
       font-style: italic;
     }
   </style>
   <slot name="menu-bar"></slot>
   <span id="contents">
   <slot>The content</slot>
 </span>

Or, you can just set the css inside javascript:

const style = document.createElement("style");
style.textContent = "* {font-style: italic}";
shadow.appendChild(style);

Example: https://jsfiddle.net/k8fxtacw/

Cihad Turhan
  • 2,749
  • 6
  • 28
  • 45
  • Thanks for your input! The thing is that I will not have control over what other devs will put in the web component and I wanted to make sure that it doesn't affect the rest of the webpage. An iframe is not an option so I wanted to try out web component but it looks like that will also not work? – rdehuyss Aug 24 '23 at 09:39