CSS 无法在自定义 HTML 元素上正常工作
CSS not working properly on Custom HTML Elements
我一直在尝试通过扩展 HTMLElement
class 来制作自定义 HTML 元素。我尝试通过链接与我的其他两个文件位于同一目录中的 CSS 文件来为其添加一些样式 - index.html
和 custom.css
.
主文件夹
- index.html
- custom.css
- custom.js
index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="nofollow" type="text/css" href=''>
</head>
<body>
<script src="./custom.js"></script>
<smooth-button text="Smooth button" no-1 = 1 no-2 = 2></smooth-button>
</body>
</html>
custom.css
:
smooth-button{
display: block;
color: blue;
background-color: orange;
}
custom.js
:
class SmoothButton extends HTMLElement{
constructor(){
super();
this.shadow = this.attachShadow({mode: "open"})
}
connectedCallback(){
this.render();
}
render(){
this.SumOfNo1AndNo2 = null;
if(this.getAttribute("no-1")!=null && this.getAttribute("no-2")!=null){
this.SumOfNo1AndNo2 = parseInt(this.getAttribute("no-1")) +
parseInt(this.getAttribute("no-2"));
}
else{
console.log("Invalid attribute.")
}
this.shadow.innerHTML = `<button>` + this.getAttribute("text") + " " + this.SumOfNo1AndNo2
+ "</button>"
}
}
customElements.define("smooth-button", SmoothButton);
有了这个,我按预期得到了一个带有文本的按钮,但样式应用于整个元素,而不是应用于组成它的元素。如何使用外部 CSS 文件将样式分别应用于其每个元素(目前只是一个 <button>
)?我正在使用外部 CSS 因为它在我阅读时更好 .
With this, I get a button as expected, with the text, but the style is applied to the element as a whole and not to the elements it's made of.
这实际上是自定义元素的工作方式。您不能将样式应用于外部文档中的阴影 DOM。如果可以,您很可能会通过外部修改破坏自定义元素样式。
然而一切并没有丢失!按钮与背景颜色不同的原因是用户代理样式表。你实际上可以设置一些CSS来告诉背景继承父背景颜色。尝试将此添加到您的自定义元素中:
const style = document.createElement('style');
style.textContent = `
button {
background: inherit;
}
`;
this.shadow.append(style);
JSFiddle:https://jsfiddle.net/5t2m3bku/
(另请注意,将 interpolate/concatenate 文本直接输入 HTML 并不是一个好主意。该文本会被解释为 HTML,这可能导致无效的 HTML 如果使用保留字符,甚至潜在的 XSS 漏洞。您可以修改设置 innerHTML
的那一行来设置文本,或者切换到模板引擎。)
除了布拉德的回答。将样式从光 DOM 应用到阴影 DOM 的方法之一是使用 CSS 变量。
smooth-button{
display: block;
--button-color: blue;
--button-background-color: orange;
}
render() {
this.shadow.innerHTML = `
<style>
button {
color: var(--button-color);
background-color: var(--button-background-color);
}
</style>
<button>
${this.getAttribute("text")} ${this.SumOfNo1AndNo2}
</button>
`;
)
除了 Brad 和 Emiel 的回答,
- (Brad) 直接在 shadowDOM 里面加一个
<style>
元素
- 一定要阅读 adopted StylesSheets(仅限 Chromium)
- (Emiel) 使用级联 CSS 属性
- 有更多选项可以设置 shadowDOM 的样式:
了解可继承样式
使用阴影部分
<style>
::part(smoothButton){
display: block;
color: blue;
background-color: orange;
}
</style>
<smooth-button></smooth-button>
<smooth-button></smooth-button>
<script>
customElements.define("smooth-button", class extends HTMLElement {
constructor(){
super()
.attachShadow({mode:"open"})
.innerHTML = `<button part="smoothButton">LABEL</button>`;
}
});
</script>
- https://developer.mozilla.org/en-US/docs/Web/CSS/::part
- https://meowni.ca/posts/part-theme-explainer/
- https://css-tricks.com/styling-in-the-shadow-dom-with-css-shadow-parts/
- https://dev.to/webpadawan/css-shadow-parts-are-coming-mi5
- https://caniuse.com/mdn-html_global_attributes_exportparts
但是...
你应该问自己的第一个问题:
Do I really need shadowDOM?
如果你不想要它的封装行为,那么不要使用shadowDOM
<style>
.smoothButton{
display: block;
color: blue;
background-color: orange;
}
</style>
<smooth-button></smooth-button>
<smooth-button></smooth-button>
<script>
customElements.define("smooth-button", class extends HTMLElement {
connectedCallback(){
this.innerHTML = `<button class="smoothButton">LABEL</button>`;
}
});
</script>
shadowDOM <slot>
另一种选择是使用 shadowDOM <slot>
元素,因为它们由其容器元素设置样式
<style>
.smoothButton{
display: block;
color: blue;
background-color: orange;
}
</style>
<smooth-button><button class="smoothButton">LABEL</button></smooth-button>
<smooth-button><button class="smoothButton">LABEL</button></smooth-button>
<script>
customElements.define("smooth-button", class extends HTMLElement {
constructor(){
super()
.attachShadow({mode:"open"})
.innerHTML = `<slot></slot>`;
}
});
</script>
当你进入 <slot>
兔子洞时,一定要阅读(很长)post:
我一直在尝试通过扩展 HTMLElement
class 来制作自定义 HTML 元素。我尝试通过链接与我的其他两个文件位于同一目录中的 CSS 文件来为其添加一些样式 - index.html
和 custom.css
.
主文件夹
- index.html
- custom.css
- custom.js
index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="nofollow" type="text/css" href=''>
</head>
<body>
<script src="./custom.js"></script>
<smooth-button text="Smooth button" no-1 = 1 no-2 = 2></smooth-button>
</body>
</html>
custom.css
:
smooth-button{
display: block;
color: blue;
background-color: orange;
}
custom.js
:
class SmoothButton extends HTMLElement{
constructor(){
super();
this.shadow = this.attachShadow({mode: "open"})
}
connectedCallback(){
this.render();
}
render(){
this.SumOfNo1AndNo2 = null;
if(this.getAttribute("no-1")!=null && this.getAttribute("no-2")!=null){
this.SumOfNo1AndNo2 = parseInt(this.getAttribute("no-1")) +
parseInt(this.getAttribute("no-2"));
}
else{
console.log("Invalid attribute.")
}
this.shadow.innerHTML = `<button>` + this.getAttribute("text") + " " + this.SumOfNo1AndNo2
+ "</button>"
}
}
customElements.define("smooth-button", SmoothButton);
有了这个,我按预期得到了一个带有文本的按钮,但样式应用于整个元素,而不是应用于组成它的元素。如何使用外部 CSS 文件将样式分别应用于其每个元素(目前只是一个 <button>
)?我正在使用外部 CSS 因为它在我阅读时更好
With this, I get a button as expected, with the text, but the style is applied to the element as a whole and not to the elements it's made of.
这实际上是自定义元素的工作方式。您不能将样式应用于外部文档中的阴影 DOM。如果可以,您很可能会通过外部修改破坏自定义元素样式。
然而一切并没有丢失!按钮与背景颜色不同的原因是用户代理样式表。你实际上可以设置一些CSS来告诉背景继承父背景颜色。尝试将此添加到您的自定义元素中:
const style = document.createElement('style');
style.textContent = `
button {
background: inherit;
}
`;
this.shadow.append(style);
JSFiddle:https://jsfiddle.net/5t2m3bku/
(另请注意,将 interpolate/concatenate 文本直接输入 HTML 并不是一个好主意。该文本会被解释为 HTML,这可能导致无效的 HTML 如果使用保留字符,甚至潜在的 XSS 漏洞。您可以修改设置 innerHTML
的那一行来设置文本,或者切换到模板引擎。)
除了布拉德的回答。将样式从光 DOM 应用到阴影 DOM 的方法之一是使用 CSS 变量。
smooth-button{
display: block;
--button-color: blue;
--button-background-color: orange;
}
render() {
this.shadow.innerHTML = `
<style>
button {
color: var(--button-color);
background-color: var(--button-background-color);
}
</style>
<button>
${this.getAttribute("text")} ${this.SumOfNo1AndNo2}
</button>
`;
)
除了 Brad 和 Emiel 的回答,
- (Brad) 直接在 shadowDOM 里面加一个
<style>
元素- 一定要阅读 adopted StylesSheets(仅限 Chromium)
- (Emiel) 使用级联 CSS 属性
- 有更多选项可以设置 shadowDOM 的样式:
了解可继承样式
使用阴影部分
<style>
::part(smoothButton){
display: block;
color: blue;
background-color: orange;
}
</style>
<smooth-button></smooth-button>
<smooth-button></smooth-button>
<script>
customElements.define("smooth-button", class extends HTMLElement {
constructor(){
super()
.attachShadow({mode:"open"})
.innerHTML = `<button part="smoothButton">LABEL</button>`;
}
});
</script>
- https://developer.mozilla.org/en-US/docs/Web/CSS/::part
- https://meowni.ca/posts/part-theme-explainer/
- https://css-tricks.com/styling-in-the-shadow-dom-with-css-shadow-parts/
- https://dev.to/webpadawan/css-shadow-parts-are-coming-mi5
- https://caniuse.com/mdn-html_global_attributes_exportparts
但是...
你应该问自己的第一个问题:
Do I really need shadowDOM?
如果你不想要它的封装行为,那么不要使用shadowDOM
<style>
.smoothButton{
display: block;
color: blue;
background-color: orange;
}
</style>
<smooth-button></smooth-button>
<smooth-button></smooth-button>
<script>
customElements.define("smooth-button", class extends HTMLElement {
connectedCallback(){
this.innerHTML = `<button class="smoothButton">LABEL</button>`;
}
});
</script>
shadowDOM <slot>
另一种选择是使用 shadowDOM <slot>
元素,因为它们由其容器元素设置样式
<style>
.smoothButton{
display: block;
color: blue;
background-color: orange;
}
</style>
<smooth-button><button class="smoothButton">LABEL</button></smooth-button>
<smooth-button><button class="smoothButton">LABEL</button></smooth-button>
<script>
customElements.define("smooth-button", class extends HTMLElement {
constructor(){
super()
.attachShadow({mode:"open"})
.innerHTML = `<slot></slot>`;
}
});
</script>
当你进入 <slot>
兔子洞时,一定要阅读(很长)post: