从外部对Shadow DOM进行样式设置

29
我正在寻找一种方法来从外部样式化影子DOM。例如,我想将所有“span.special”元素中的所有文本颜色设置为红色,包括来自影子DOM的“span.special”元素。我该怎么做?
以前有::shadow伪元素和/deep/组合器(也称为>>>)用于此目的。因此,我可以编写类似以下内容的代码:
span.special, *::shadow span.special {
    color: red
}

但是现在::shadow/deep/>>>已经被弃用。那么,我们有什么替代它们的方法呢?

5个回答

15

我尝试了很多方法,包括这里描述的方法。由于我正在使用外部Web组件库,我无法访问修改这些组件。因此,对我有效的唯一解决方案是使用JS querySelector,像这样:

document.querySelector("the-element.with-shadow-dom")
  .shadowRoot.querySelector(".some-selector").setAttribute("style", "color: black");

这个解决方案并不是最好的,不适用于大型样式,但对于小的增强效果非常有效。

@John,我已经在Chrome 83.0.4103.116上进行了测试(接下来还要在Safari上测试),并且我使用了Ionic(v5)的ion-toast组件。这里是我使用的(几乎)实际代码:

  import { toastController } from '@ionic/core';

  let toastOpts = {
    message: "Some message goes here.",
    cssClass: "toast-with-vertical-buttons",
    buttons: [
      {
        text: "Button 1",
        side: 'end'
      },
      {  
        text: "Button2",
        side: 'end'
      },
      {
        icon: "close",
        side: "start"
      }
    ]
  }
  toastController.create(toastOpts).then(async p => {
    let toast = await p.present(); // this renders ion-toast component and returns HTMLIonToastElement
    toast.shadowRoot.querySelector('div.toast-button-group-end').setAttribute("style", "flex-direction: column");
  });

请提供一个通用的HTML5元素示例,并澄清它在哪些浏览器中可以工作? - John
1
似乎可以工作,尽管我只对像“input”元素的影子DOM进行样式设置感兴趣,特别是在Waterfox 56中。不过,我还没有看到其他人发布像你这样的代码,谢谢。 - John

12
仍然没有简单的方法穿透影子根,但是以下是三种可行的方法。请记住,您需要在Web组件内部进行更改。
  1. 使用变量 v1 - 您需要传递属性并在Web组件内部使用变量。
  2. 使用变量 v2 - 您需要在Web组件内部使用变量。
  3. 使用 ::part() - 您需要向您想要在Web组件中设置样式的元素添加一个part属性。(注意:这个伪元素得到了很好的支持,但仍处于实验模式,因此请确保在生产中使用之前了解它)。
有关详细信息,请运行下面的代码示例。
const elA = document.querySelector('custom-container-a');
const shadowRootA = elA.attachShadow({mode:'open'});
shadowRootA.innerHTML = '<style>:host([border]) {display:block;border: var(--custom-border);}</style>'+
    '<p>Shadow content A</p>'
  
  
const elB = document.querySelector('custom-container-b');
const shadowRootB = elB.attachShadow({mode:'open'});
shadowRootB.innerHTML = '<style>p {display:block;color: var(--custom-color, blue);}</style>'+
    '<p>Shadow content B</p>'


const elC = document.querySelector('custom-container-c');
const shadowRootC = elC.attachShadow({mode:'open'});
shadowRootC.innerHTML = '<p part="paragraph">Shadow content C</p>'
/* Normal way of styling */
p {
  color: orange;
}

/* Using variables version 1 */
custom-container-a {
  --custom-border: 3px solid gold;
}

/* Using variables version 2 */
custom-container-b {
  --custom-color: green;
}


/* Using ::part() */
custom-container-c::part(paragraph) {
  color: magenta;
}
<p>Light content</p>
<custom-container-a border></custom-container-a>
<custom-container-b></custom-container-b>
<custom-container-c></custom-container-c>


4
阴影部分现在得到了相当好的支持: https://caniuse.com/mdn-css_selectors_part - prideout

9
您可以使用@import css,如此答案中对其他SO问题的解释。
将该规则包含在阴影树中的style元素中。
 <style>
   @import url( '/css/external-styles.css' )
 </style>

注意,>>> combinator 仍是CSS作用域模块草案的一部分。

1
同样地,将 <style /> 替换为 <link rel="stylesheet" href="/css/external-styles.css" /> 也应该可以正常工作。 - Heechul Ryu

7

嗯,如果你正在使用无法更改的库Web组件,@import并不是解决方案...

最终我找到了几种方法:

1)级联。Shadow DOM的宿主元素样式也会影响Shadow DOM元素。如果你需要对Shadow DOM的特定元素进行样式设置,而不是所有元素,则不能使用此选项。

2)自定义属性。如果Web组件的作者提供了这样做的方法,可以使用自定义属性

3)在Polymer中,还有自定义混合选项 https://www.polymer-project.org/1.0/docs/devguide/styling

4)@import,但仅适用于非库组件

因此,有几种可能性,但它们都是有限制的。没有强大到像::shadow那样的外部样式表。


0
我使用了以下解决方案,因为它甚至适用于库组件,并且比通过JS手动样式化所有内容更简单。
编写一个名为"customization.css"的文件,其中包含您想要嵌入到影子根中的样式。
然后通过JS将<link rel="stylesheet" href="customization.css">添加到影子根中。
let lnk = document.createElement("link");
lnk.rel = "stylesheet";
lnk.href = "link to your customization.css";

let customElement = document.querySelector("your-custom-element");
customElement.shadowRoot.insertBefore(lnk, customElement.shadowRoot.firstChild);

注意:我还没有在封闭的影子根中进行过测试。我认为它不会起作用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接