将样式导入 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 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">
它已被 whatwg 和 W3C 批准。
在影子 dom 中使用 css 的有用 link:
- https://w3c.github.io/webcomponents/spec/shadow/#inertness-of-html-elements-in-a-shadow-tree
- https://github.com/whatwg/html/commit/43c57866c2bbc20dc0deb15a721a28cbaad2140c
- https://github.com/w3c/webcomponents/issues/628
直接csslink可以影子dom.
可构造样式sheets
这是一项允许构建 CSSStyleSheet
对象的新功能。这些可以使用 JavaScript 从 css 文件设置或导入它们的内容,并应用于文档和 Web 组件的影子根。它将在 Chrome 版本 73 中可用,并且可能在不久的将来用于 Firefox。
有一个good writeup on the Google developers site,但我会在下面用一个例子在下面简要总结一下。
创建样式sheet
您通过调用构造函数创建一个新的 sheet:
const sheet = new CSSStyleSheet();
设置和替换样式:
可以通过调用方法 replace
或 replaceSync
.
来应用样式
replaceSync
是同步的,不能使用任何外部资源:
sheet.replaceSync(`.redText { color: red }`);
replace
是 asynchronous 并且可以接受引用外部资源的 @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;
}
}
}
}
}
将样式导入 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 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">
它已被 whatwg 和 W3C 批准。
在影子 dom 中使用 css 的有用 link:
- https://w3c.github.io/webcomponents/spec/shadow/#inertness-of-html-elements-in-a-shadow-tree
- https://github.com/whatwg/html/commit/43c57866c2bbc20dc0deb15a721a28cbaad2140c
- https://github.com/w3c/webcomponents/issues/628
直接csslink可以影子dom.
可构造样式sheets
这是一项允许构建 CSSStyleSheet
对象的新功能。这些可以使用 JavaScript 从 css 文件设置或导入它们的内容,并应用于文档和 Web 组件的影子根。它将在 Chrome 版本 73 中可用,并且可能在不久的将来用于 Firefox。
有一个good writeup on the Google developers site,但我会在下面用一个例子在下面简要总结一下。
创建样式sheet
您通过调用构造函数创建一个新的 sheet:
const sheet = new CSSStyleSheet();
设置和替换样式:
可以通过调用方法 replace
或 replaceSync
.
replaceSync
是同步的,不能使用任何外部资源:sheet.replaceSync(`.redText { color: red }`);
replace
是 asynchronous 并且可以接受引用外部资源的@import
语句。请注意replace
returns aPromise
需要相应地处理。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;
}
}
}
}
}