Search code examples
javascripthtmlweb-componentcustom-element

HTML web component does not use shadow DOM style


I have created a vanilla web component or HTML element. It just displays two links.

To encapsulate the thing, I use shadow DOM. However it does not seem to be encapsulated. In the DOM tree it's inside #shadow-root which is good.

Why does the web component use the global style instead of the style I provided in the template for my web component?

The text is red and I expected it to be green.

class MyEl extends HTMLElement {
  constructor() {
    super();
    this.shadow = this.attachShadow({ mode: "open" });
  }

  connectedCallback() {
    const template = `
      <style>
        a {
          color: green;
        }
      </style>
      <slot></slot>`;
    this.shadow.innerHTML = template;
  }
}

window.customElements.define("my-el", MyEl);
a {
  color: red
}
  <my-el>
    <a href="example.com">Item1</a>
    <a href="example.com">Item2</a>
  </my-el>


Solution

  • While this question already has an accepted answer, moving a slot's children to the shadowRoot isn't desirable for most use cases.

    What you probably want to do is to use the ::slotted() selector.

    Just bear in mind that styles applied to a slot's children through the ::slotted() selector only act as "default" styles and can still be overridden by using styles in light DOM.

    For example, check this edited version of your snippet:

    As you can see, this time my-el tries to apply both a color and a text-decoration style to anchor (<a>) children in any of it's slots.

    However, in light dom, we have a a.special selector that overrides the color, so the <a class="special"> will be red, not green

    class MyEl extends HTMLElement {
      constructor() {
        super();
        this.shadow = this.attachShadow({ mode: "open" });
      }
    
      connectedCallback() {
        const template = `
          <style>
            ::slotted(a) {
              color: green;
              text-decoration: none;
            }
          </style>
          <slot></slot>`;
        this.shadow.innerHTML = template;
      }
    }
    
    window.customElements.define("my-el", MyEl);
    a.special {
      color: red
    }
      <my-el>
        <a href="example.com">Item1</a>
        <a class="special" href="example.com">Item2</a>
      </my-el>