:: slotted CSS селектор для вложенных дочерних элементов в слот shadowDOM - PullRequest
7 голосов
/ 06 мая 2020

Селектор CSS ::slotted выбирает дочерние элементы элемента <slot>.

однако при попытке выбрать внуков, например, с помощью ::slotted(*), ::slotted(*) * или ::slotted(* *), селектор не действует.

class MyElement extends HTMLElement {
  constructor() {
    super();
    const shadowRoot = this.attachShadow({mode: 'open'})
    shadowRoot.innerHTML = `
      <style>
        ::slotted(*) {
          display: block;
          border: solid blue 1px;
          padding: 3px;
        }
        ::slotted(*) span {
          display: block;
          border: solid red 1px;
          padding: 3px;
        }
        ::slotted(* span) {
          display: block;
          border: solid green 1px;
          padding: 3px;
        }
      </style>
      <slot></slot>
    `;
  }
}
customElements.define('my-element', MyElement);
<my-element>
  <p>
    <span>Test</span>
  </p>
</my-element>

Обратите внимание, как диапазон не имеет границы.

Это ожидаемое поведение? Мне не удалось найти конкретную документацию по этому поводу.

Если да, есть ли способ обойти это?

1 Ответ

9 голосов
/ 06 мая 2020

стили :: элементы с прорезями в shadowDOM

TL; DR

  • содержимое с ячейками остается в lightDOM, это отражение на <slot>

  • ::slotted(*) можно только нацеливать на lightDOM SKIN с простыми селекторами


фон

Да, ::slotted() отсутствие стилизации вложенных элементов является ожидаемым поведением.

Термин slotted противоречит интуиции, это означает, что элемент lightDOM перемещен в shadowDOM

слот lightDOM НЕ перемещен , он остается .. скрытым .. в lightDOM содержимое (если разделено на IF): отражено в <slot></slot>

Или из Документация разработчика Google

????????????, ??????????? ????? ??? ???? ? ??? ???????. ????? ???'? ?????????? ???? ???; ???? ?????? ?? ?? ??????? ???????? ?????? ??? ?????? ???.

Я использую термин отраженный вместо render , потому что render подразумевает вас может получить к нему доступ в shadowDOM. Вы не можете, потому что содержимое слотов не в shadowDOM ... только отражено от lightDOM.


Почему: слот имеет ограниченную функциональность

Был опробован более продвинутый стиль shadowDOM.

Веб-компоненты версии 0 (v0) имели <content> и ::content; но его удалили из spe c: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/content

Основной вывод из обсуждения стандартов W3 C (@hayatoito (команда Google) здесь и здесь ):

So in V1 we have :slotted: https://developer.mozilla.org/en-US/docs/Web/CSS/ :: прорези


Дополнение №1: Производительность if :: slotted разрешена для сложных селекторов

От разработчика Mozilla Эмилио:

источник: https://github.com/w3c/webcomponents/issues/889

Проблема с производительностью заключается в том, что он увеличивает количество поддеревьев, в которых каждый узел должен go искать правила, которые на них влияют.

Прямо сейчас logi c выглядит так: если вы проходите через свои слоты и собирайте правила в их теневых деревьях по мере необходимости. Это код Это хорошо, потому что сложность стилизации элемента напрямую зависит от сложности теневых деревьев, которые вы строите, и затрагивает только узлы с прорезями.

Если вы Если вы хотите разрешить комбинаторам проходить через слоты, тогда каждому узлу нужно будет посмотреть на своих предков и предыдущих братьев и сестер и посмотреть, какие из них имеют слоты, а затем проделать этот процесс для всех своих слотов. Затем, в верхней части, вам также необходимо изменить общий код сопоставления селекторов, чтобы селекторы, не содержащие селекторов со слотами, не совпадали, если вы не находитесь в правом теневом дереве.

Это стоимость, которая вы платите за все элементы, независимо от того, используете ли вы Shadow DOM или :: slotted, и, вероятно, просто не полетит.


Так что из-за проблем с производительностью

:slotted( S ) ограничена CSS функциональность селектора:

  • ► для S требуются только простые селекторы -> В основном все, что содержит пробел, не будет работать

  • ► нацелен только на lightDOM 'skin' . -> Другими словами, работает только первый уровень

<my-element>
  <h1>Hello World</h1> 
  <p class=foo>
    <span>....</span>
  </p>
  <p class=bar>
    <span>....</span>
  </p>
</my-element>
  • ::slotted(h1) и ::slotted(p)

  • ::slotted(.foo) работает

  • ::slotted(span) (или что-то более глубокое) не будет работать (кроме 'skin' element)

Примечание: ::slotted([Simple Selector]) соответствует правилам специфичности, но (будучи простым) не добавляет веса селекторам lightDOM skin , поэтому никогда не получает более высокую специфичность. Вам может понадобится !important в некоторых (редких) случаях использования.

 <style>
  ::slotted(H1) {
    color: blue !important;
  }
 <style>

Стилизация содержимого с слотами

См. Также: Применение дополнительных выбор глубины для: host CSS псевдокласс

# 1 - стиль lightDOM

<span> скрыт в lightDOM, любые сделанные там изменения будут продолжать отражать его слот представление.

Это означает, что вы можете применить любой стиль вы хотите с CSS в основном DOM (или родительский контейнер shadowDOM, если вы обернули <my-element> в один)

 <style>
  my-element span {
    .. any CSS you want
  }
 <style>

# 2 - (обходной путь) переместите lightDOM в shadowDOM

Если вы переместите lightDOM с appendChild или append (или insertBefore или cloneNode, et c.) из lightDOM в shadowDOM, вы можете делать все, что захотите.

Без использования <slot></slot> и :slotted()

# 3 - :: part (Shadow Parts)

Это другой / мощный способ стилизации содержимого shadowDOM:

Apple наконец реализовала это в Safari 13.1, март 2020

см .:

Примечание! ::part стили shadowDOM , <slot></slot> содержимое остается в lightDOM !


ссылки

имейте в виду: может содержать документацию v0!


Пример: использование слотов в качестве маршрутизатора

Изменить имя слота при нажатии кнопки и извлечение нового контента из lightDOM:

<template id=MY-ELEMENT>
  <style>
    ::slotted([slot="Awesome"]){
      background:lightgreen
    }
  </style>
  <slot><!-- all unslotted content goes here --></slot>
  <slot id=answer name=unanswered></slot>
</template>
<style>/* style all IMGs in lightDOM */
  img { max-height: 165px;border:3px dashed green }
  img:hover{ border-color:red }
</style>
<my-element><!-- content below is: lightDOM! -->
  SLOTs are: <button>Cool</button> <button>Awesome</button> <button>Great</button>
  <span slot=unanswered>?</span>
  <div  slot=Cool>   <img src="https://i.imgur.com/VUOujQT.jpg"></div>
  <span slot=Awesome><b>SUPER!</b></span>
  <div  slot=Awesome><img src="https://i.imgur.com/y95Jq5x.jpg"></div>
  <div  slot=Great>  <img src="https://i.imgur.com/gUFZNQH.jpg"></div>
</my-element>
<script>
  customElements.define('my-element', class extends HTMLElement {
    connectedCallback() {
      this.attachShadow({mode:'open'})
          .append(document.getElementById(this.nodeName).content.cloneNode(true));
      this.onclick = (evt) => {
           const label = evt.composedPath()[0].innerText; // Cool,Awesome,Great
           this.shadowRoot.getElementById("answer").name = label;
      }
    }
  });
</script>

Больше ответов, связанных с SLOT, можно найти с помощью поиска StackOverflow: Пользовательские элементы SLOT

...