2

我在将我的 webcomponent 实例的采用样式表添加到文档中时遇到问题。

我的自定义 web 组件在构造函数中创建了一个新的 CSSStyleSheet 实例。当组件被 connectedCallback 升级时,它会将实例添加到 document.adoptedStyleSheets 中。然后我有一个 update() 方法,稍后可以调用它来更改所采用样式表的 css 属性。

在以下示例中,您可以看到用法。我的 HTML 包含两个自定义组件和一些用于添加和删除样式属性的按钮。

我们可以使用添加按钮为这两个组件添加不同的样式。一切正常,直到开发人员属性未打开。如果您打开开发人员属性并重新加载示例,似乎添加到采用的样式表的两个 CSSStyleSheet 实例都引用了同一个实例。

单击元素 1 旁边的“应用样式”按钮将为元素 1 添加一个绿色边框。

单击元素 2 旁边的“应用样式”按钮将为元素 2 添加黑色边框。

但它删除了元素 1 的样式。这不应该发生,因为元素 1 的 CSSStyleSheet 对象仍然应该具有自己的 css 属性。但他们迷路了。

class CustomElem extends HTMLElement {
  
  constructor() {
    super();
    this.myStyle = new CSSStyleSheet();
  }

  connectedCallback() {
    document.adoptedStyleSheets = [ ...document.adoptedStyleSheets, this.myStyle ];
  }
  
  update(css) {
    this.myStyle.replaceSync(css);
  }
}

customElements.define('custom-elem', CustomElem);

document.getElementById('btn1_add').addEventListener('click', function() {
  document.getElementById('elem1').update('custom-elem#elem1 { border: 3px solid green; }');
});

document.getElementById('btn2_add').addEventListener('click', function() {
  document.getElementById('elem2').update('custom-elem#elem2 { border: 3px solid black; }')
});

document.getElementById('btn1_remove').addEventListener('click', function() {
  document.getElementById('elem1').update('');
});

document.getElementById('btn2_remove').addEventListener('click', function() {
  document.getElementById('elem2').update('')
});
<html>
  <head></head>
  <body>
    Custom Elements<br /><br/>
    <custom-elem id="elem1">Element 1</custom-elem>
    <input type="button" id="btn1_add" value="Apply Style"></input>
    <input type="button" id="btn1_remove" value="Remove Style"></input>
    <br /><br />
    <custom-elem id="elem2">Element 2</custom-elem>
    <input type="button" id="btn2_add" value="Apply Style"></input>
    <input type="button" id="btn2_remove" value="Remove Style"></input>   
  </body>
</html>

我的第二个示例解决了这个问题。如果我直接在 connectedCallback 中调用 replaceSync(''),everythink 会像预期的那样工作。

class CustomElem extends HTMLElement {
  
  constructor() {
    super();
    this.myStyle = new CSSStyleSheet();
  }

  connectedCallback() {
    document.adoptedStyleSheets = [ ...document.adoptedStyleSheets, this.myStyle ];    
    // *****************************
    // this makes it work!
    // *****************************
    this.myStyle.replaceSync('');
  }
  
  update(css) {
    this.myStyle.replaceSync(css);
  }
}

customElements.define('custom-elem', CustomElem);

document.getElementById('btn1_add').addEventListener('click', function() {
  document.getElementById('elem1').update('custom-elem#elem1 { border: 3px solid green; }');
});

document.getElementById('btn2_add').addEventListener('click', function() {
  document.getElementById('elem2').update('custom-elem#elem2 { border: 3px solid black; }')
});

document.getElementById('btn1_remove').addEventListener('click', function() {
  document.getElementById('elem1').update('');
});

document.getElementById('btn2_remove').addEventListener('click', function() {
  document.getElementById('elem2').update('')
});
<html>
  <head></head>
  <body>
    Custom Elements<br /><br/>
    <custom-elem id="elem1">Element 1</custom-elem>
    <input type="button" id="btn1_add" value="Apply Style"></input>
    <input type="button" id="btn1_remove" value="Remove Style"></input>
    <br /><br />
    <custom-elem id="elem2">Element 2</custom-elem>
    <input type="button" id="btn2_add" value="Apply Style"></input>
    <input type="button" id="btn2_remove" value="Remove Style"></input>
  </body>
</html>

谁能告诉我,为什么会这样?

4

1 回答 1

0

根据您在 bugs.chromium.org 上报告的问题,这实际上是 Chrome 回归,引入了与 Chrome 85 中的样式表突变相关的更改。它在 85 之前已经正常工作。

顺便说一句,非常好的复制器!

修复程序已经实施,可以使用Chrome Canary进行验证。该修复程序将登陆 Chrome 88,它应该在 2021 年 1 月 19 日成为稳定版本(请参阅发布)。

于 2020-10-20T08:57:03.187 回答