skip to Main Content

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?

2

Answers


  1. 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/

    Login or Signup to reply.
  2. 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 );
      });
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search