将样式导入 Web 组件

Importing styles into a web component

将样式导入 Web 组件的规范方法是什么?

下面给我报错HTML element <link> is ignored in shadow tree:

<template>
    <link rel="style" href="foo.css" />
    <h1>foo</h1>
</template>

我正在使用阴影 DOM 插入此内容,使用以下内容:

var importDoc, navBarProto;

importDoc = document.currentScript.ownerDocument;

navBarProto = Object.create(HTMLElement.prototype);
navBarProto.createdCallback = function() {
  var template, templateClone, shadow;

  template = importDoc.querySelector('template');
  templateClone = document.importNode(template.content, true);

  shadow = this.createShadowRoot();
  shadow.appendChild(templateClone);
};

document.registerElement('my-nav-bar', {
  prototype: navBarProto
});

注意!!!

此答案已过时

请检查下方 Himanshu Sharma 的回答

最新答案:

根据Polymer documentation:

Polymer allows you to include stylesheets in your <polymer-element> definitions, a feature not supported natively by Shadow DOM.

这是一个有点奇怪的参考,但我无法 google 直接参考。看起来目前没有关于支持模板内部链接的传言。

就是说,无论您想使用原始 Web 组件,您都应该使用 <style> 标记内联 css,或者加载并应用 css 手动在 javascript.

尝试 <template> 中的 <style> 元素:

<template>
    <style>
       h1 { 
         color: red;
         font-family: sans-serif;
       }
    </style>
    <h1>foo</h1>
</template>

如果您需要在 <template> 标签内放置外部样式,您可以尝试

<style> @import "../my/path/style.css"; </style>

不过我觉得这将在元素创建后开始导入。

现在阴影 dom 中支持直接 <link> 标签。

一个可以直接使用:

<link rel="stylesheet" href="yourcss1.css">
<link href="yourcss2.css" rel="stylesheet" type="text/css">  

它已被 whatwgW3C 批准。

在影子 dom 中使用 css 的有用 link:

直接csslink可以影子dom.

可构造样式sheets

这是一项允许构建 CSSStyleSheet 对象的新功能。这些可以使用 JavaScript 从 css 文件设置或导入它们的内容,并应用于文档和 Web 组件的影子根。它将在 Chrome 版本 73 中可用,并且可能在不久的将来用于 Firefox。

有一个good writeup on the Google developers site,但我会在下面用一个例子在下面简要总结一下。

创建样式sheet

您通过调用构造函数创建一个新的 sheet:

const sheet = new CSSStyleSheet();

设置和替换样式:

可以通过调用方法 replacereplaceSync.

来应用样式
  • replaceSync 是同步的,不能使用任何外部资源:
    sheet.replaceSync(`.redText { color: red }`);
    
  • replaceasynchronous 并且可以接受引用外部资源的 @import 语句。请注意 replace returns a Promise 需要相应地处理。
    sheet.replace('@import url("myStyle.css")')
      .then(sheet => {
        console.log('Styles loaded successfully');
      })
      .catch(err => {
        console.error('Failed to load:', err);
      });
    

正在将样式应用于文档或阴影DOM

可以通过设置 document 或阴影 DOM 的 adoptedStyleSheets 属性来应用样式。

document.adoptedStyleSheets = [sheet]

adoptedStyleSheets中的数组被冻结,不能用push()改变,但你可以通过结合它的现有值来连接:

document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];

从文档继承

阴影 DOM 可以用相同的方式从文档的 adoptedStyleSheets 继承构造的样式:

// in the custom element class:
this.shadowRoot.adoptedStyleSheets = [...document.adoptedStyleSheets, myCustomSheet];

请注意,如果这是 运行 在构造函数中,组件将仅继承在其创建之前采用的样式 sheet。在 connectedCallback 中设置 adoptedStyleSheets 将在连接时为每个实例继承。值得注意的是,这不会导致 FOUC.

Web 组件示例

让我们创建一个名为 x-card 的组件,将文本包装成样式精美的 div

// Create the component inside of an IIFE
(function() {
  // template used for improved performance
  const template = document.createElement('template');
  template.innerHTML = `
    <div id='card'></div>
  `;

  // create the stylesheet
  const sheet = new CSSStyleSheet();
  // set its contents by referencing a file
  sheet.replace('@import url("xCardStyle.css")')
  .then(sheet => {
    console.log('Styles loaded successfully');
  })
  .catch(err => {
    console.error('Failed to load:', err);
  });

  customElements.define('x-card', class extends HTMLElement {
    constructor() {
      super();
      this.attachShadow({
        mode: 'open'
      });
      // apply the HTML template to the shadow DOM
      this.shadowRoot.appendChild(
        template.content.cloneNode(true)
      );
      // apply the stylesheet to the shadow DOM
      this.shadowRoot.adoptedStyleSheets = [sheet];
    }

    connectedCallback() {
      const card = this.shadowRoot.getElementById('card');
      card.textContent = this.textContent;
    }
  });

})();
<x-card>Example Text</x-card>
<x-card>More Text</x-card>

以上答案显示了如何将样式表导入 Web 组件,但可以通过编程方式(某种程度上)将单个样式导入影子 DOM。这是我最近开发的技术。

首先 - 确保使用 HTML 代码将组件本地样式直接嵌入到模板中。这是为了确保阴影 DOM 在您的元素构造函数中有一个样式表。 (引入其他样式表应该没问题,但你必须在构造函数中准备好)

第二个 - 使用css-变量指向要导入的css规则。

#rule-to-import {
   background-color: #ffff00;
}

my-element {
   --my-import: #rule-to-import;
}

Third - 在组件构造函数中,读取 CSS 变量并在文档样式表中找到指向的样式。找到后,复制字符串但重写选择器以匹配您希望设置样式的内部元素。我为此使用了一个辅助函数。

importVarStyle(shadow,cssvar,target) {
    // Get the value of the specified CSS variable
    const varstyle=getComputedStyle(this).getPropertyValue(cssvar).trim();
    if(varstyle!="") varstyle: {
        const ownstyle=shadow.styleSheets[0];
        for(let ssheet of document.styleSheets) {   // Walk through all CSS rules looking for a matching rule
            for(let cssrule of ssheet.cssRules) {
                if(cssrule.selectorText==varstyle) {    // If a match is found, re-target and clone the rule into the component-local stylesheet
                    ownstyle.insertRule(
                        cssrule.cssText.replace(/^[^{]*/,target),
                        ownstyle.cssRules.length
                    );
                    break varstyle;
                }
            }
        }
    }
}