::slotted CSS 选择器用于 shadowDOM 插槽中的嵌套子级

Posted

技术标签:

【中文标题】::slotted CSS 选择器用于 shadowDOM 插槽中的嵌套子级【英文标题】:::slotted CSS selector for nested children in shadowDOM slot 【发布时间】:2020-08-20 21:38:42 【问题描述】:

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】:

在 shadowDOM 中设置 ::slotted 元素的样式

TL;DR

::slotted 规格:https://drafts.csswg.org/css-scoping/#slotted-pseudo

插入的内容保留light DOM中,反映到@ shadow DOM

中的 987654348@

::slotted(x) 以 lightDOM 外部元素(又名“皮肤”)为目标,不是 shadowDOM 中的 SLOT

::slotted(x) 接受basic selectors

可继承的样式渗入 shadowDOMhttps://lamplightdev.com/blog/2019/03/26/why-is-my-web-component-inheriting-styles/

有关 SLOT 和相关主题的最新 WHATWG 讨论,请参阅

https://github.com/whatwg/html/issues/6051#issuecomment-816971072 参与者:rniwa(Apple)、annvk(Mozilla)、dominic(Google) https://github.com/WICG/webcomponents/issues/934#issuecomment-906063140

背景

是的,::slotted() 不设置嵌套元素的样式是预期行为。

slotted 这个词是违反直觉的, 这意味着元素 lightDOM 被 移动 到 shadowDOM

开槽的 lightDOM 没有移动,它仍然是..隐藏的..在 lightDOM 内容(IF 时隙)反映&lt;slot&gt;&lt;/slot&gt;

或来自Google Developer Documentation

????????????,???????????????????????????。 ????? ???'? ?????????? ???? ???;

我使用术语 reflected 而不是 render,因为 render 意味着您可以在 in shadowDOM 中访问它。您不能,因为槽位内容不在 shadowDOM 中...仅从 lightDOM 中反映


为什么 :slotted 功能有限

尝试了更高级的 shadowDOM 样式。

WebComponents 版本 0 (v0) 有 &lt;content&gt;::content;但它已从规范中删除:https://developer.mozilla.org/en-US/docs/Web/HTML/Element/content

W3C 标准讨论的主要内容 (@hayatoito(Google 团队)here 和 here)是:

所以在 V1 中我们有 :slotted: https://developer.mozilla.org/en-US/docs/Web/CSS/::slotted


补充 #1:如果 ::slotted 允许复杂选择器的性能

来自 Mozilla 开发者 Emilio:

来源:https://github.com/w3c/webcomponents/issues/889

性能问题是它增加了子树的数量 每个节点都需要去寻找影响它们的规则。

现在的逻辑是这样的:如果你有槽,遍历你的槽 并根据需要在他们的影子树中收集规则。 This is the code 这很好,因为元素样式的复杂性 直接取决于您所使用的阴影树的复杂性 构建,它只影响开槽节点。

如果你想允许组合子通过时隙,那么每个节点都会 需要查看它的祖先和前兄弟链并查看哪个 其中一些被开槽,然后对所有槽执行该过程。 然后,最重要的是,您还需要更改通用选择器匹配 代码使不包含开槽选择器的选择器不 如果您不在正确的阴影树中,则匹配。

这是您为所有元素支付的成本,无论您是否 使用 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' 元素)

注意: ::slotted([Simple Selector]) 确认符合特异性规则, 但是(很简单)不会向 lightDOM skin 选择器添加权重,因此永远不会获得更高的特异性。 在某些(罕见的)用例中,您可能需要!important

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

样式化插槽内容

另见:Applying more in depth selection to the :host CSS pseudo class

#1 - 样式 lightDOM

&lt;span&gt; 隐藏在 lightDOM 中,在那里所做的任何更改都将继续反映到它的 slotted 表示。

这意味着您可以在主 DOM 中使用 CSS 应用任何您想要的样式 (如果您将 &lt;my-element&gt; 包装在一个容器中,则为父 shadowDOM 容器)

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

#2 - (解决方法)将 lightDOM 移动到 shadowDOM

如果您移动 lightDOM shadowDOM 使用:this.shadowRoot.append(...this.childNodes)

你可以在 shadowDOM &lt;style&gt; 标签中做所有你想要的样式。

注意:您现在可以使用 &lt;slot&gt;&lt;/slot&gt;:slotted()&lt;slot&gt;s 仅适用于来自 lightDOM 的反射内容。

例如,元素将 自身 包裹在一个额外的 shadowDOM 层中, 所以 no CSS 会溢出,可以使用 &lt;slot&gt;s,请参阅:

https://jsfiddle.net/CustomElementsExamples/Lhcsd2m5/?slotmeister

#3 - ::part(阴影部分)

这是一种不同的/强大的方式来设置 shadowDOM 内容的样式:

Apple 终于在 2020 年 3 月的 Safari 13.1 中实现了 shadowParts

见:

https://meowni.ca/posts/part-theme-explainer/

https://css-tricks.com/styling-in-the-shadow-dom-with-css-shadow-parts/

https://dev.to/webpadawan/css-shadow-parts-are-coming-mi5

https://caniuse.com/mdn-html_global_attributes_exportparts

注意! ::part 样式 shadowDOM&lt;slot&gt;&lt;/slot&gt; 内容仍保留在 lightDOM 中!


参考

注意:可能包含 v0 文档!

https://css-tricks.com/encapsulating-style-and-structure-with-shadow-dom/

https://developers.google.com/web/fundamentals/web-components/shadowdom?hl=en#composition_slot

https://polymer-library.polymer-project.org/2.0/docs/devguide/style-shadow-dom#style-your-elements

https://github.com/w3c/webcomponents/issues/331

https://github.com/w3c/webcomponents/issues/745

https://developer.mozilla.org/en-US/docs/Web/API/HTMLSlotElement/slotchange_event

::part() - https://developer.mozilla.org/en-US/docs/Web/CSS/::part


示例:将插槽用作路由器

在 buttonclick 上更改 slot-name 和 reflect 来自 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>

【讨论】:

关于性能评论,没有提供任何证据。我们需要看到数字。网络中有很多“慢”的 API,如果使用得当,它们也不是坏事。 @trusktr 是的……性能不佳是删除功能的可怕理由。例如:动画宽度的表现非常糟糕,但是否由开发人员决定。 @FeldsLiscia 如此真实。在大多数情况下,即使在最慢的低端手机上,甚至在智能手表上,这样的动画也能正常工作。动画一两件东西不同于动画 100 或 1000 件东西。开发人员应该有选项,并且知道何时使用它们。文档在这里很重要。

以上是关于::slotted CSS 选择器用于 shadowDOM 插槽中的嵌套子级的主要内容,如果未能解决你的问题,请参考以下文章

Vue2、Vue3中 CSS 穿透(深度选择器)

css选择器的1.13 UI元素状态伪类选择器

css 用于自定义结帐页面的CSS选择器。

CSS里的HTML选择器、类选择器、ID选择器用于哪些范围?

用于在 PHP 中解析 HTML 的 CSS 选择器

CSS 是不是有一个选择器用于在多个类中进行选择? [复制]