css - 向 shadowDOM 中动态创建的样式表添加规则
问题描述
我最近开始接触 WebComponents,我必须说到目前为止我发现它具有开创性!为了更好地理解这个概念,我尝试将我早期项目中的设计融入可重用的 Web 组件中。我首先尝试创建一个带有一些附加功能的 Table 元素。
首先,我为组件声明了一个最小模板:
<template id="data-table-template">
<table>
<thead></thead>
<tbody></tbody>
<tfoot></tfoot>
</table>
</template>
我使用此模板通过将表头值和行值作为属性来创建表。该元素的使用方式如下:
<eur-data-table
headers="getHeaders()",
rowdata="getRowData()"
host="#data-table-host"
template="#data-table-template"
></eur-data-table>
在组件原型connectedCallback
方法中,我为组件添加标题、行和设置样式。Chrome Element 检查器生成的 shadow DOM 片段如下:
<eur-data-table headers="getHeaders()" ,="" rowdata="getRowData()" host="#data-table-host" template="#data-table-template" theme="firebrick">
<table>
<style id="styleBlock"></style>
<thead>
<th>Name</th>
<th>Age</th>
</thead>
<tbody>
<tr>
<td>Babu</td>
<td>60</td>
</tr>
<tr>
<td>Shyam</td>
<td>35</td>
</tr>
</tbody>
<tfoot></tfoot>
</table>
</eur-data-table>
问题在于添加样式规则。在组件类中,我创建了一个style
元素并将其附加到影子 DOM,然后再向style.sheet
属性添加规则。我检查了控制台输出,style.sheet
看起来所有规则都已添加。但是,在将样式元素插入到 shadow DOM 之后,从上面渲染的输出中可以看出没有任何规则。
在组件类中,通过以下方式添加样式:
connectedCallback() {
let template = document.querySelector(DataTable._parseId(this.template));
if(!template) throw new DOMException('Host and/or template ids are undefined');
if(!this.hasAttribute('theme')) this.setAttribute('theme', 'firebrick');
let style = document.createElement('style');
style.setAttribute('id', 'styleBlock');
// WebKit Hack
style.appendChild(document.createTextNode(""));
this.shadowRoot.appendChild(style);
this._table = template.content.cloneNode(true);
this._setupHeaders();
this._setupBody();
this._setupStyle(style.sheet); // style is not being applied!!
console.dir(style.sheet); // logs a CSSStyleSheet object
this.shadowRoot.appendChild(this._table);
}
在该connectedCallback
方法中,_setupStyle
传递一个对样式表的引用,它以以下方式添加规则(规则表示为 javascript 对象):
_setupStyle(sheet) {
if(!sheet) throw new DOMException('sheet is undefined', 'StylesheetNotFoundException');
let rules = window.themes[this.theme];
if(!rules) throw new DOMException('Theme is undefined');
Object.keys(rules)
.sort((r1, r2) => rules[r1].order - rules[r2].order)
.forEach(rule => {
let o = _.omit(rules[rule], 'order');
let i = rule.order;
let s = this._makeRule(o, rule);
sheet.insertRule(s, i);
});
}
该order
值是一个整数,用于按顺序对样式规则进行排序。排序后,在order
将对象转换为 DOMString 之前,会从对象中省略键及其值:
_makeRule(value, rule) {
return `${rule} ${JSON.stringify(value).replace(new RegExp(/,/g), ';')}`;
}
记录style.sheet
属性打印:
CSSStyleSheet
cssRules: CSSRuleList
0: CSSStyleRule {selectorText: "tfoot", style: CSSStyleDeclaration, styleMap: StylePropertyMap, type: 1, cssText: "tfoot { }", …}
1: CSSStyleRule {selectorText: "tr:last-child > td", style: CSSStyleDeclaration, styleMap: StylePropertyMap, type: 1, cssText: "tr:last-child > td { }", …}
2: CSSStyleRule {selectorText: "td:last-child", style: CSSStyleDeclaration, styleMap: StylePropertyMap, type: 1, cssText: "td:last-child { }", …}
3: CSSStyleRule {selectorText: "td", style: CSSStyleDeclaration, styleMap: StylePropertyMap, type: 1, cssText: "td { }", …}
4: CSSStyleRule {selectorText: "tr:first-child", style: CSSStyleDeclaration, styleMap: StylePropertyMap, type: 1, cssText: "tr:first-child { }", …}
5: CSSStyleRule {selectorText: "tr:last-child", style: CSSStyleDeclaration, styleMap: StylePropertyMap, type: 1, cssText: "tr:last-child { }", …}
6: CSSStyleRule {selectorText: "tr", style: CSSStyleDeclaration, styleMap: StylePropertyMap, type: 1, cssText: "tr { }", …}
7: CSSStyleRule {selectorText: "th:last-child", style: CSSStyleDeclaration, styleMap: StylePropertyMap, type: 1, cssText: "th:last-child { }", …}
8: CSSStyleRule {selectorText: "th", style: CSSStyleDeclaration, styleMap: StylePropertyMap, type: 1, cssText: "th { }", …}
9: CSSStyleRule {selectorText: "thead", style: CSSStyleDeclaration, styleMap: StylePropertyMap, type: 1, cssText: "thead { }", …}
10: CSSStyleRule {selectorText: "thead tbody tfoot", style: CSSStyleDeclaration, styleMap: StylePropertyMap, type: 1, cssText: "thead tbody tfoot { }", …}
11: CSSStyleRule {selectorText: "table", style: CSSStyleDeclaration, styleMap: StylePropertyMap, type: 1, cssText: "table { }", …}
我的问题是:由于组件没有错误地呈现,并且因为我按预期添加了所有规则并将样式和表格元素附加到影子 DOM,然后按预期呈现元素,为什么我的 CSS 规则不是应用于渲染的内容?
解决方案
好的,我终于找到了一个可行的解决方案。innerHTML
在样式标签的属性中添加样式要容易得多。为此,我需要将_setupStyle
and_makeRule
方法(发布在问题中)更改为以下内容:
_setupStyle(style) {
if(!style) throw new DOMException('sheet is undefined', 'StylesheetNotFoundException');
let rules = window.themes[this.theme];
if(!rules) throw new DOMException('Theme is undefined');
style.innerHTML = "";
Object.keys(rules)
.sort((r1, r2) => rules[r1].order - rules[r2].order)
.forEach(rule => {
let o = _.omit(rules[rule], 'order');
let i = rule.order;
let s = this._makeRule(o, rule);
//sheet.insertRule(s, i);
style.innerHTML += s + ' \n';
});
}
我不再使用CSSStyleSheet
类来添加规则(已注释掉);相反,我只是在新行上添加每个规则。_makeRule
方法如下:
_makeRule(value, rule) {
return `${rule} ${JSON.stringify(value).replace(this._cssParseRule, ';').replace(/\'|\"/g, "")}`;
}
我修改了将对象转换为字符串的方法,然后替换,
为,;
然后替换全部'
或"
。渲染的输出如下:
<style id="styleBlock">
table {border:1px solid firebrick;border-collapse:collapse;border-spacing:0;padding:0}
thead tbody tfoot {margin:0;padding:0}
thead {border-bottom:1px solid firebrick;background-color:firebrick;color:azure}
th {border-right:1px solid firebrick;border-bottom:1px solid firebrick}
th:last-child {border-bottom:none}
tr {margin:0;border-bottom:1px solid firebrick}
tr:last-child {border-bottom:none}
tr:first-child {border-left:none}
td {margin:0;padding:10px;text-align:center;border-left:1px solid firebrick;border-right:1px solid firebrick;border-bottom:1px solid firebrick}
td:last-child {border-right:none}
tr:last-child > td {border-bottom:none}
tfoot {margin:0;border-top:1px solid firebrick}
</style>
注意:虽然 CSS 可以工作,但它是不正确的;;
每个规则的最后一个属性的末尾应该有一个。
推荐阅读
- java - How to read this JSON with Java?
- javascript - javascript object property cannot be defined
- php - 让 Laravel Accessor 返回查询结果
- jquery - What is the interaction between _Layout.cshtml and my ViewFile.cshtml?
- python - Python - matlibplot - 控制图形大小
- django - How to serve another static root in Django for html pages?
- mysql - 将不同表的多个数据值存储为父表中的单个值 - 数据库关系
- django-cms - 模块“html5lib.filters.sanitizer”没有属性“allowed_elements”
- php - IntlDateFormatter 不返回特定的非位置时区格式 (z)
- python - 有没有办法可以加快这段代码的速度,或者这是最快的?