Веб-компонент HTML не использует стиль теневой DOM

Aug 15 2020

Я создал обычный веб-компонент или HTML-элемент. Он просто отображает две ссылки.

Чтобы инкапсулировать вещь, я использую теневой DOM. Однако, похоже, он не инкапсулирован. В дереве DOM он находится внутри # shadow-root, что хорошо.

Почему веб-компонент использует глобальный стиль вместо стиля, который я предоставил в шаблоне для моего веб-компонента?

Текст красный, и я ожидал, что он будет зеленым.

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>

Ответы

1 AlanDávalos Aug 17 2020 at 15:12

Хотя на этот вопрос уже есть принятый ответ, перемещение дочерних элементов слота в shadowRoot нежелательно для большинства случаев использования.

Вероятно, вы захотите использовать ::slotted()селектор.

Просто имейте в виду, что стили, применяемые к дочерним элементам слота через ::slotted()селектор, действуют только как стили «по умолчанию» и могут быть отменены с помощью стилей в облегченной модели DOM.

Например, проверьте эту отредактированную версию вашего фрагмента:

Как видите, на этот раз my-elпытается применить и цвет, и стиль оформления текста к дочерним <a>элементам anchor ( ) в любом из своих слотов.

Однако в light dom у нас есть a.specialселектор, который переопределяет цвет, поэтому <a class="special">он будет красным, а не зеленым.

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

Полное, подробное объяснение находится в: :: Селектор CSS со слотами для вложенных потомков в слот shadowDOM.

TL; DR

Ваши ссылки находятся в lightDOM и, таким образом, стилизованы под его DOM (в вашем коде - документ DOM).
Перемещение узлов из lightDOM в shadowDOM - одно из «решений»; но тогда вы не используете слоты.

К вашему сведению, ваш код можно сжать до:

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

  }
}

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

Больше ответов, связанных с SLOT, можно найти с помощью StackOverflow Search: Custom Elements SLOTs

KresimirPendic Aug 15 2020 at 19:57

обратите внимание на эту строку, вы должны перемещать / копировать элементы в тень, например, с помощью:

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

Я добавил это, чтобы продемонстрировать, что к элементам shadow dom будет применяться только встроенный стиль ... поэтому скопированные ссылки в SD используют ваш стиль :)

так красный будет GLOBAL, зеленый будет SHADOWэлементами

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>