O componente HTML da web não usa o estilo DOM sombra

Aug 15 2020

Eu criei um componente da web vanilla ou elemento HTML. Ele apenas exibe dois links.

Para encapsular a coisa, eu uso o DOM sombra. No entanto, não parece estar encapsulado. Na árvore DOM está dentro de # shadow-root, o que é bom.

Por que o componente da web usa o estilo global em vez do estilo que forneci no modelo para meu componente da web?

O texto está vermelho e eu esperava que fosse verde.

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>

Respostas

1 AlanDávalos Aug 17 2020 at 15:12

Embora essa pergunta já tenha uma resposta aceita, mover os filhos de um slot para o shadowRoot não é desejável para a maioria dos casos de uso.

O que você provavelmente deseja fazer é usar o ::slotted()seletor.

Lembre-se de que os estilos aplicados aos filhos de um slot por meio do ::slotted()seletor funcionam apenas como estilos "padrão" e ainda podem ser substituídos pelo uso de estilos no DOM claro.

Por exemplo, verifique esta versão editada do seu snippet:

Como você pode ver, desta vez my-eltenta aplicar uma cor e um estilo de decoração de texto aos <a>filhos anchor ( ) em qualquer um de seus slots.

No entanto, no dom de luz, temos um a.specialseletor que substitui a cor, então o <a class="special">será vermelho, não verde

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>

1 Danny'365CSI'Engelman Aug 15 2020 at 23:44

A explicação completa e detalhada está em: :: seletor CSS com slot para filhos aninhados no slot shadowDOM

TL; DR

Seus links estão em lightDOM e, portanto, estilizados por seu DOM (em seu código, o DOM do documento)
Mover os nós de lightDOM para shadowDOM é uma "solução"; mas você não está usando slots então.

Para sua informação, seu código pode ser compactado para:

class MyEl extends HTMLElement {
  constructor() {
    super().attachShadow({ mode: "open" })
           .innerHTML = `<style>a{color:green}</style><slot></slot>`;

  }
}

window.customElements.define("my-el", MyEl);

Mais respostas relacionadas a SLOT podem ser encontradas com StackOverflow Search: Custom Elements SLOTs

KresimirPendic Aug 15 2020 at 19:57

observe esta linha, você deve mover / copiar elementos para a sombra, por exemplo com:

this.shadow.innerHTML = this.innerHTML + template;

Eu adicionei isso para demonstrar que apenas o estilo embutido será aplicado aos elementos do shadow dom .. então, os links copiados em SD estão usando o seu estilo :)

então o vermelho será GLOBAL, o verde serão os SHADOWelementos

class MyEl extends HTMLElement {
  constructor() {
    super();
  }

  connectedCallback() {
    this.shadow = this.attachShadow({ mode: "open" });
    const template = `
      <style>
        a {
          color: green;
        }
      </style>
      <slot></slot>`;
    this.shadow.innerHTML = this.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>