<slot>
有意设计为允许外部代码对放入其中的内容进行样式设置。如果使用得当,这是一个很棒的功能。
但是,如果您想更好地控制 Web 组件中显示的内容,则需要将内容的克隆副本从this.childNodes
影子 DOM 中复制出来。然后你就可以 100% 控制 CSS。
好的。你真的只有 90% 的控制权,因为使用你的组件的人仍然可以设置style
属性。
customElements.define("my-nav",
class extends HTMLElement {
constructor() {
super();
const template = document.querySelector("template#my-nav").content;
this.attachShadow({ mode: "open" })
.appendChild(template.cloneNode(true));
}
connectedCallback() {
var container = this.shadowRoot.querySelector('.links-container');
var children = this.childNodes;
if (children.length > 0 && container) {
while(container.firstChild) {
container.removeChild(container.firstChild);
}
for (var i = 0; i < children.length; i++) {
container.appendChild(children[i].cloneNode(true));
}
}
}
}
);
a {
color: red;
}
<template id="my-nav">
<style>
.links-container a {
color: lime;
font-weight: bold;
margin-right: 20px;
}
</style>
<div class="links-container">
</div>
</template>
<p>I want these links to be green:</p>
<my-nav>
<a href="#">Link 1</a>
<a href="#">Link 2</a>
<a href="#" style="color: red">Link 3</a>
</my-nav>
正如您在上面的示例中看到的,第三个链接仍然是红色的,因为我们设置了style
属性。
如果您想防止这种情况发生,那么您需要style
从内部内容中去除该属性。
customElements.define("my-nav",
class extends HTMLElement {
constructor() {
super();
const template = document.querySelector("template#my-nav").content;
this.attachShadow({ mode: "open" })
.appendChild(template.cloneNode(true));
}
connectedCallback() {
var container = this.shadowRoot.querySelector('.links-container');
var children = this.childNodes;
if (children.length > 0 && container) {
while(container.firstChild) {
container.removeChild(container.firstChild);
}
for (var i = 0; i < children.length; i++) {
container.appendChild(children[i].cloneNode(true));
}
container.querySelectorAll('[style]').forEach(el => el.removeAttribute('style'));
}
}
}
);
a {
color: red;
}
<template id="my-nav">
<style>
.links-container a {
color: lime;
font-weight: bold;
margin-right: 20px;
}
</style>
<div class="links-container">
</div>
</template>
<p>I want these links to be green:</p>
<my-nav>
<a href="#">Link 1</a>
<a href="#">Link 2</a>
<a href="#" style="color: red">Link 3</a>
</my-nav>
我什至创建了一些组件,这些组件允许我读入独特的子节点并将其转换为自定义内部节点。
想想<video>
标签和它的<source>
孩子。这些孩子并没有真正渲染任何东西,它们只是一种保存用于指示要播放的视频的源位置的数据的方式。
这里的关键是了解<slot>
应该用于什么,并且只以这种方式使用它,而不是试图强迫它做一些它从未打算做的事情。
奖励积分
由于ConnectedCallback
每次将此节点放入 DOM 时都会调用,因此每次都必须小心删除 shadow DOM 中的任何内容,否则您将一遍又一遍地复制子节点。
customElements.define("my-nav",
class extends HTMLElement {
constructor() {
super();
const template = document.querySelector("template#my-nav").content;
this.attachShadow({ mode: "open" })
.appendChild(template.cloneNode(true));
}
connectedCallback() {
var container = this.shadowRoot.querySelector('.links-container');
var children = this.childNodes;
if (children.length > 0 && container) {
for (var i = 0; i < children.length; i++) {
container.appendChild(children[i].cloneNode(true));
}
}
}
}
);
function reInsert() {
var el = document.querySelector('my-nav');
var parent = el.parentNode;
el.remove();
parent.appendChild(el);
}
setTimeout(reInsert, 1000);
setTimeout(reInsert, 2000);
a {
color: red;
}
<template id="my-nav">
<style>
.links-container a {
color: lime;
font-weight: bold;
margin-right: 20px;
}
</style>
<div class="links-container">
</div>
</template>
<p>I want these links to be green:</p>
<my-nav>
<a href="#">Link 1</a>
<a href="#">Link 2</a>
<a href="#" style="color: red">Link 3</a>
</my-nav>
所以删除重复的节点很重要:
customElements.define("my-nav",
class extends HTMLElement {
constructor() {
super();
const template = document.querySelector("template#my-nav").content;
this.attachShadow({ mode: "open" })
.appendChild(template.cloneNode(true));
}
connectedCallback() {
var container = this.shadowRoot.querySelector('.links-container');
var children = this.childNodes;
if (children.length > 0 && container) {
while(container.firstChild) {
container.removeChild(container.firstChild);
}
for (var i = 0; i < children.length; i++) {
container.appendChild(children[i].cloneNode(true));
}
}
}
}
);
function reInsert() {
var el = document.querySelector('my-nav');
var parent = el.parentNode;
el.remove();
parent.appendChild(el);
}
setTimeout(reInsert, 1000);
setTimeout(reInsert, 2000);
a {
color: red;
}
<template id="my-nav">
<style>
.links-container a {
color: lime;
font-weight: bold;
margin-right: 20px;
}
</style>
<div class="links-container">
</div>
</template>
<p>I want these links to be green:</p>
<my-nav>
<a href="#">Link 1</a>
<a href="#">Link 2</a>
<a href="#" style="color: red">Link 3</a>
</my-nav>