如何让光 dom css 应用于阴影 dom 元素?
how to let light dom css apply on shadow dom elements?
我定义了我自己的自定义元素并发现在阴影 dom 中添加它的 children 元素很方便,主要是因为我可以在自定义元素构造函数中添加它,而完全相同的构造函数不允许我添加 "regular" children.
自定义元素的构造函数不允许您添加 children 因为新元素在创建后应该是空的。
我想知道是否有一种简单的方法可以 css 文档中的样式可以影响阴影中的元素 dom。我希望来自灯光 dom 的选择器能够到达阴影 dom.
中的这些元素
我的自定义元素之一是 "item"。我将创建很多它们来填充列表。我不喜欢在每个项目的实例阴影中重复完全相同的样式标签 dom,所以我正在寻找一个地方为我的所有项目放置这个样式标签。
我读了很多关于阴影 dom 以及内部样式如何不影响边界外元素的文章,但其中任何一篇都回答了我的问题。
有什么想法吗?谢谢!
当前版本的 shadowDOM 要求您将 CSS 放在 shadowDOM 内。
大多数 CSS 相当小,每个元素仅添加几个字节到几百个字节。我见过的一些最大的副本在每个副本中添加了大约 2k 的 CSS。但这与DOM结构中表示的数据相比还是很小的。
有一些东西会从外面渗透出来,比如字体信息,但不是很多。
您可以通过以下方式从外部影响:
1。 CSS 个变量
A CSS Variable 允许您将变量设置为在 CSS 中使用的值,无论是否在影子 DOM 中。
2。属性
可以捕获属性并将其迁移到影子中DOM CSS。我有一些组件使用属性来定义主题。
3。属性
也可以获取属性并将其应用于内部 CSS。
还有其他的方法正在讨论中,但是要等到V2了。
class MyEL extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'}).innerHTML = `
<style>
.outer {
color: var(--myColor, 'black');
</style>
<div class="outer">
<h4>Title</h4>
<slot></slot>
</div>`;
}
static get observedAttributes() {
return ['bgcolor'];
}
attributeChangedCallback(attrName, oldVal, newVal) {
if (oldVal !== newVal) {
this.shadowRoot.querySelector('.outer').style.backgroundColor = newVal;
}
}
get border() {
return this.shadowRoot.querySelector('.outer').style.border;
}
set border(value) {
this.shadowRoot.querySelector('.outer').style.border = value;
}
}
customElements.define('my-el', MyEL);
setTimeout(() => {
document.querySelector('my-el').border = '2px dashed blue';
},1000);
const btn = document.getElementById('toggle');
let color = '';
btn.addEventListener('click', () => {
color = color === '' ? 'white' : '';
document.querySelector('my-el').style.setProperty('--myColor', color);
});
<my-el bgcolor="red"></my-el>
<hr/>
<button id="toggle">toggle</button>
已更新
正如我在下面的评论中所述,有一些 CSS 规则将穿透阴影 DOM 如果它们不被覆盖。穿透的东西是color
、background
、font
,以及其他与之相关的东西。
此代码显示了它们如何渗透的示例:
customElements.define('css-test', class extends HTMLElement {
constructor() {
super();
var shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `<h1>Header</h1>
<p>This is a <strong>strong</strong> string</p>
<p>This is <em>emphasis<em>.`;
}
});
const styles = document.createElement('style');
styles.textContent = `
* {
background-color: #900;
border: 1px solid #9876;
border-radius: 5px;
box-sizing: border-box;
box-shadow: 0 0 4px #0005;
color: #fff;
font: 24px Tahoma;
margin: 20px;
padding 20px;
text-align: center;
text-decoration: #ffd wavy underline;
text-shadow: 3px 3px 3px #0008;
transform: rotate(30deg);
}`;
function toggleCss(evt) {
if(styles.parentElement) {
styles.remove();
}
else {
document.body.appendChild(styles);
}
}
const toggleEl = document.getElementById('toggle');
toggleEl.addEventListener('click', toggleCss);
<css-test></css-test>
<hr/>
<button id="toggle">Toggle CSS</button>
当您点击切换按钮时,您会看到颜色、背景颜色、字体等发生了变化。但是像旋转这样的事情只会发生在组件上,不会发生在组件内的子元素上。这是一件好事。想象一下,用户可以在您的组件内旋转东西……您的整个组件都会损坏。
我一直在寻找一篇很好的文章,它谈到了所有穿透但找不到的东西。以前看过,但是忘记收藏了。因此,如果有人找到该文章,请将其添加到下面的评论中。
现在这里是上面相同的代码,但有几处被覆盖,因此您可以看到您的组件如何覆盖外部所做的事情:
customElements.define('css-test', class extends HTMLElement {
constructor() {
super();
var shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `<style>
:host * {
background: white;
color: yellow;
font-size: 14px;
}
</style><h1>Header</h1>
<p>This is a <strong>strong</strong> string</p>
<p>This is <em>emphasis<em>.`;
}
});
const styles = document.createElement('style');
styles.textContent = `
* {
background-color: #900;
border: 1px solid #9876;
border-radius: 5px;
box-sizing: border-box;
box-shadow: 0 0 4px #0005;
color: #fff;
font: 24px Tahoma;
margin: 20px;
padding 20px;
text-align: center;
text-decoration: #ffd wavy underline;
text-shadow: 3px 3px 3px #0008;
transform: rotate(30deg);
}`;
function toggleCss(evt) {
if(styles.parentElement) {
styles.remove();
}
else {
document.body.appendChild(styles);
}
}
const toggleEl = document.getElementById('toggle');
toggleEl.addEventListener('click', toggleCss);
<css-test></css-test>
<hr/>
<button id="toggle">Toggle CSS</button>
首先,我不知道您所说的自定义组件应该为空是什么意思...我不认为这是真的,因为我找不到任何说明该要求的文档。此外,以下对我来说很好用:
customElements.define("my-element", class extends HTMLElement {
constructor() {
super();
// Add a `span` element to the component's light-DOM.
const span = document.createElement("span");
span.innerHTML = "Hello, World!";
this.appendChild(span);
// Add a `slot` element to the component's shadow-DOM (so that the light-DOM gets rendered).
this.attachShadow({ mode: "open" }).innerHTML = "<slot></slot>";
}
});
<my-element></my-element>
如果你的意思是light-DOM内容如果不能赋值给shadow-DOM中的一个slot
元素就不会被渲染,那么你是对的,的当然。
总之,那只是一个副节点。回到关于从 Web 组件外部设置阴影样式的问题-DOM...
除了,你还可以看看web组件的::part
和::theme
伪元素
像这样:
customElements.define("my-element", class extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" }).innerHTML = `
<style>
* {
position: relative;
box-sizing: border-box;
margin: 0;
padding: 0;
}
:host {
display: inline-block;
height: 150px;
width: 200px;
}
#outer {
height: 100%;
padding: 20px;
background: lightblue;
border: 5px solid blue;
}
#inner {
height: 100%;
background: lightgreen;
border: 5px solid green;
}
</style>
<div id="outer" part="foo">
<div id="inner" part="bar">
</div>
</div>
`;
}
});
#second {
width: 300px;
}
#second::part(bar) {
background: lightcoral;
border: 10px dotted red;
}
<my-element id="first"></my-element>
<my-element id="second"></my-element>
更详尽的解释,我想参考:
- Monica Dinculescu's article "::part and ::theme, an ::explainer" 在她的博客上发布,
- Explainer: CSS Shadow ::part and ::theme,基于 Monica 的文章,但已更新以符合最新规范。
请注意,::part
伪元素和相应的 part
属性目前仅在 Chrome 和 Opera 中有效。 FireFox 将在版本 69 中支持它,但首先你必须在其 about:config
页面中将 layout.css.shadow-parts.enabled
设置为 true
(这让我怀疑它对使用 Firefox 的普通用户是否非常有用, 所以我希望 Mozilla 尽快默认打开它)。
我定义了我自己的自定义元素并发现在阴影 dom 中添加它的 children 元素很方便,主要是因为我可以在自定义元素构造函数中添加它,而完全相同的构造函数不允许我添加 "regular" children.
自定义元素的构造函数不允许您添加 children 因为新元素在创建后应该是空的。
我想知道是否有一种简单的方法可以 css 文档中的样式可以影响阴影中的元素 dom。我希望来自灯光 dom 的选择器能够到达阴影 dom.
中的这些元素我的自定义元素之一是 "item"。我将创建很多它们来填充列表。我不喜欢在每个项目的实例阴影中重复完全相同的样式标签 dom,所以我正在寻找一个地方为我的所有项目放置这个样式标签。
我读了很多关于阴影 dom 以及内部样式如何不影响边界外元素的文章,但其中任何一篇都回答了我的问题。
有什么想法吗?谢谢!
当前版本的 shadowDOM 要求您将 CSS 放在 shadowDOM 内。
大多数 CSS 相当小,每个元素仅添加几个字节到几百个字节。我见过的一些最大的副本在每个副本中添加了大约 2k 的 CSS。但这与DOM结构中表示的数据相比还是很小的。
有一些东西会从外面渗透出来,比如字体信息,但不是很多。
您可以通过以下方式从外部影响:
1。 CSS 个变量
A CSS Variable 允许您将变量设置为在 CSS 中使用的值,无论是否在影子 DOM 中。
2。属性
可以捕获属性并将其迁移到影子中DOM CSS。我有一些组件使用属性来定义主题。
3。属性
也可以获取属性并将其应用于内部 CSS。
还有其他的方法正在讨论中,但是要等到V2了。
class MyEL extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'}).innerHTML = `
<style>
.outer {
color: var(--myColor, 'black');
</style>
<div class="outer">
<h4>Title</h4>
<slot></slot>
</div>`;
}
static get observedAttributes() {
return ['bgcolor'];
}
attributeChangedCallback(attrName, oldVal, newVal) {
if (oldVal !== newVal) {
this.shadowRoot.querySelector('.outer').style.backgroundColor = newVal;
}
}
get border() {
return this.shadowRoot.querySelector('.outer').style.border;
}
set border(value) {
this.shadowRoot.querySelector('.outer').style.border = value;
}
}
customElements.define('my-el', MyEL);
setTimeout(() => {
document.querySelector('my-el').border = '2px dashed blue';
},1000);
const btn = document.getElementById('toggle');
let color = '';
btn.addEventListener('click', () => {
color = color === '' ? 'white' : '';
document.querySelector('my-el').style.setProperty('--myColor', color);
});
<my-el bgcolor="red"></my-el>
<hr/>
<button id="toggle">toggle</button>
已更新
正如我在下面的评论中所述,有一些 CSS 规则将穿透阴影 DOM 如果它们不被覆盖。穿透的东西是color
、background
、font
,以及其他与之相关的东西。
此代码显示了它们如何渗透的示例:
customElements.define('css-test', class extends HTMLElement {
constructor() {
super();
var shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `<h1>Header</h1>
<p>This is a <strong>strong</strong> string</p>
<p>This is <em>emphasis<em>.`;
}
});
const styles = document.createElement('style');
styles.textContent = `
* {
background-color: #900;
border: 1px solid #9876;
border-radius: 5px;
box-sizing: border-box;
box-shadow: 0 0 4px #0005;
color: #fff;
font: 24px Tahoma;
margin: 20px;
padding 20px;
text-align: center;
text-decoration: #ffd wavy underline;
text-shadow: 3px 3px 3px #0008;
transform: rotate(30deg);
}`;
function toggleCss(evt) {
if(styles.parentElement) {
styles.remove();
}
else {
document.body.appendChild(styles);
}
}
const toggleEl = document.getElementById('toggle');
toggleEl.addEventListener('click', toggleCss);
<css-test></css-test>
<hr/>
<button id="toggle">Toggle CSS</button>
当您点击切换按钮时,您会看到颜色、背景颜色、字体等发生了变化。但是像旋转这样的事情只会发生在组件上,不会发生在组件内的子元素上。这是一件好事。想象一下,用户可以在您的组件内旋转东西……您的整个组件都会损坏。
我一直在寻找一篇很好的文章,它谈到了所有穿透但找不到的东西。以前看过,但是忘记收藏了。因此,如果有人找到该文章,请将其添加到下面的评论中。
现在这里是上面相同的代码,但有几处被覆盖,因此您可以看到您的组件如何覆盖外部所做的事情:
customElements.define('css-test', class extends HTMLElement {
constructor() {
super();
var shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `<style>
:host * {
background: white;
color: yellow;
font-size: 14px;
}
</style><h1>Header</h1>
<p>This is a <strong>strong</strong> string</p>
<p>This is <em>emphasis<em>.`;
}
});
const styles = document.createElement('style');
styles.textContent = `
* {
background-color: #900;
border: 1px solid #9876;
border-radius: 5px;
box-sizing: border-box;
box-shadow: 0 0 4px #0005;
color: #fff;
font: 24px Tahoma;
margin: 20px;
padding 20px;
text-align: center;
text-decoration: #ffd wavy underline;
text-shadow: 3px 3px 3px #0008;
transform: rotate(30deg);
}`;
function toggleCss(evt) {
if(styles.parentElement) {
styles.remove();
}
else {
document.body.appendChild(styles);
}
}
const toggleEl = document.getElementById('toggle');
toggleEl.addEventListener('click', toggleCss);
<css-test></css-test>
<hr/>
<button id="toggle">Toggle CSS</button>
首先,我不知道您所说的自定义组件应该为空是什么意思...我不认为这是真的,因为我找不到任何说明该要求的文档。此外,以下对我来说很好用:
customElements.define("my-element", class extends HTMLElement {
constructor() {
super();
// Add a `span` element to the component's light-DOM.
const span = document.createElement("span");
span.innerHTML = "Hello, World!";
this.appendChild(span);
// Add a `slot` element to the component's shadow-DOM (so that the light-DOM gets rendered).
this.attachShadow({ mode: "open" }).innerHTML = "<slot></slot>";
}
});
<my-element></my-element>
如果你的意思是light-DOM内容如果不能赋值给shadow-DOM中的一个slot
元素就不会被渲染,那么你是对的,的当然。
总之,那只是一个副节点。回到关于从 Web 组件外部设置阴影样式的问题-DOM...
除了::part
和::theme
伪元素
像这样:
customElements.define("my-element", class extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" }).innerHTML = `
<style>
* {
position: relative;
box-sizing: border-box;
margin: 0;
padding: 0;
}
:host {
display: inline-block;
height: 150px;
width: 200px;
}
#outer {
height: 100%;
padding: 20px;
background: lightblue;
border: 5px solid blue;
}
#inner {
height: 100%;
background: lightgreen;
border: 5px solid green;
}
</style>
<div id="outer" part="foo">
<div id="inner" part="bar">
</div>
</div>
`;
}
});
#second {
width: 300px;
}
#second::part(bar) {
background: lightcoral;
border: 10px dotted red;
}
<my-element id="first"></my-element>
<my-element id="second"></my-element>
更详尽的解释,我想参考:
- Monica Dinculescu's article "::part and ::theme, an ::explainer" 在她的博客上发布,
- Explainer: CSS Shadow ::part and ::theme,基于 Monica 的文章,但已更新以符合最新规范。
请注意,::part
伪元素和相应的 part
属性目前仅在 Chrome 和 Opera 中有效。 FireFox 将在版本 69 中支持它,但首先你必须在其 about:config
页面中将 layout.css.shadow-parts.enabled
设置为 true
(这让我怀疑它对使用 Firefox 的普通用户是否非常有用, 所以我希望 Mozilla 尽快默认打开它)。