El componente web HTML no usa el estilo DOM de sombra

Aug 15 2020

He creado un componente web vanilla o un elemento HTML. Solo muestra dos enlaces.

Para encapsular la cosa, uso shadow DOM. Sin embargo, no parece estar encapsulado. En el árbol DOM está dentro de # shadow-root, lo cual es bueno.

¿Por qué el componente web usa el estilo global en lugar del estilo que proporcioné en la plantilla para mi componente web?

El texto es rojo y esperaba que fuera 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>

Respuestas

1 AlanDávalos Aug 17 2020 at 15:12

Si bien esta pregunta ya tiene una respuesta aceptada, mover los hijos de una ranura a shadowRoot no es deseable para la mayoría de los casos de uso.

Lo que probablemente quieras hacer es usar el ::slotted()selector.

Solo tenga en cuenta que los estilos aplicados a los hijos de un espacio a través del ::slotted()selector solo actúan como estilos "predeterminados" y aún pueden anularse usando estilos en DOM ligero.

Por ejemplo, consulte esta versión editada de su fragmento:

Como puede ver, esta vez my-elintenta aplicar un color y un estilo de decoración de texto para anclar ( <a>) a los niños en cualquiera de sus espacios.

Sin embargo, en light dom, tenemos un a.specialselector que anula el color, por lo <a class="special">que será rojo, no 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

La explicación completa y detallada se encuentra en: :: selector CSS ranurado para niños anidados en la ranura shadowDOM

TL; DR

Sus enlaces están en lightDOM y por lo tanto están diseñados por su DOM (en su código el documento DOM).
Mover los nodos de lightDOM a shadowDOM es una "solución"; pero entonces no estás usando ranuras.

Para su información, su código se puede compactar para:

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

  }
}

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

Se pueden encontrar más respuestas relacionadas con SLOT con StackOverflow Search: SLOT de elementos personalizados

KresimirPendic Aug 15 2020 at 19:57

observe esta línea, debe mover / copiar elementos para sombrear, por ejemplo con:

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

Agregué esto para demostrar que solo se aplicará el estilo en línea a los elementos de dom de sombra ... así que los enlaces copiados en SD están usando su estilo :)

así que el rojo será GLOBAL, el verde será 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>