覆盖影子根元素中的样式

Override styles in a shadow-root element

有没有办法改变阴影元素中的样式?具体来说,extend/overwrite 在 css class?我正在使用一个名为 Beanote 的 chrome-extension,它自 4 月(2017 年)以来就没有更新过,并且有一个我想修复的讨厌的错误。我发现一行 css 对我来说已经足够了,但是我在应用它时不知所措,而不是进入阴影元素本身并直接在开发工具中编辑这些样式。

我正在为此寻找方法:

/*global css rule*/
.the-class-name { property-name: my-value; }

覆盖这个:

/* style tag inside the shadow-root */
.the-class-name { property-name: bad-value; }


我在网上找到的大多数资源涉及 shadow-root override styleedit shadow-root styling 的查询都与 :host 有关,如果它是用于此目的,则不适合我的需要或已弃用::shadow 等功能。

由于样式隔离是 Shadow DOM 的一个特性,因此您无法定义将在 Shadow DOM 范围内应用的全局 CSS 规则。

使用 CSS 变量是可能的,但它们应该在隐藏组件中显式实现(这不是第 3 方库的情况)。

解决方法是直接在阴影中注入样式线 DOM。

//host is the element that holds the shadow root:
var style = document.createElement( 'style' )
style.innerHTML = '.the-class-name { property-name: my-value; }'
host.shadowRoot.appendChild( style )

注意:仅当 Shadow DOM mode 设置为 'open'.

时才会起作用

2019 年更新 Chrome 73+ 和 Opera 60+

现在可以直接实例化 CSSStyleSheet 对象并将其影响到阴影 DOM 或文档:

var sheet = new CSSStyleSheet
sheet.replaceSync( `.color { color: pink }`)
host.shadowRoot.adoptedStyleSheets = [ sheet ] 

Ionic V4 select 向下图标颜色变化示例

document.querySelector('#my-select').shadowRoot.querySelector('.select-icon-inner').setAttribute('style', 'opacity:1');


ionViewDidEnter() {
    document.querySelector('#my-select').shadowRoot.querySelector('.select-icon-inner').setAttribute('style', 'opacity:1');
  }

如果要覆盖默认生成的 shadowRoot 样式,则必须在页面完全加载后调用 js 函数。

扩展之前的答案。

外部样式总是胜过影子 DOM 中定义的样式,即当您添加引用您正在设置样式的组件的全局样式规则时。参见:https://developers.google.com/web/fundamentals/web-components/shadowdom#stylefromoutside

否则这将取决于元素阴影 DOM 是否嵌入了 styleSheet,或者它是否采用 style-sheet 使用 adoptedStyleSheets.

如果元素嵌入到元素中,您可以使用 addRuleinsertRule 向现有 style-sheet 添加或插入规则。这也适用于添加 adopedStyleSheets.

的 style-sheets

如前一个答案所述,您可以将新的 style-sheet 添加到已采用的 style-sheet 列表中。这在 shadowRoot 包含嵌入的 styleSheet 时也有效,因为 adoptedStyleSheets 优先,而 styleSheetList 是 read-only 属性.

assert(myElement.shadowRoot.styleSheets.length != 0);
myElement.shadowRoot.styleSheets[0].addRule(':host', 'display: none;');

assert(myElement.shadowRoot.adoptedStyleSheets.length != 0);
`myElement.shadowRoot.adoptedStyleSheets[0].addRule(':host', 'display: none;');`

const sheet = new CSSStyleSheet();
sheet.replaceSync(`:host { display: none; }`);

const elemStyleSheets = myElement.shadowRoot.adoptedStyleSheets;

// Append your style to the existing style sheet.
myElement.shadowRoot.adoptedStyleSheets = [...elemStyleSheets, sheet];

// Or if just overwriting a style set in the embedded `styleSheet`
myElement.shadowRoot.adoptedStyleSheets = [sheet];

我想附和@Renato 在其中一条评论中给出的答案,因为它指出了解决从托管应用程序自定义 WebComponent 问题的好方法,恕我直言。

@Supersharp 是正确的,外部 CSS 规则 不会 传播到你的 Shadow Root,这是设计使然。

CSS 变量是一个很好的方向,但根据我的个人经验,对于单一用法的值来说有点矫枉过正,而且是的,它们必须被 WebComponent up-front 支持.

通过 继承 通过 :host 传播 属性(正如@Renato 提到的那样)是,恕我直言,完美正确的图案与 API 设计对齐:

  • 自定义元素的 (:host) CSS 规则在设计上可被外部规则覆盖
  • :host的children,阴影DOM的内部内容,可以默认继承:host的CSS规则或者通过明确的规则 - 这也是设计

我想说,在适用的情况下,最好在考虑 CSS 样式表注入之前采用这种方法,并且也不会受到仅 open 模式的限制。

当然,这种方法在以下情况下无济于事:

  • 内部元素没有从 :host
  • 继承相关规则
  • WebComponent 的结构相当复杂,所以单独 :host 根本帮不上忙

然而,再次根据我自己的经验,具有理想的可覆盖 CSS 规则的简单组件可能会从通过 :host.

传播规则的 non-intrusive 模式中受益匪浅