假设我有一些这样的代码:
class MyElem extends HTMLElement {
constructor() {
super();
let templateContent = document.getElementById('template-elem').content;
this.innerHTML = templateContent.cloneNode(true);
}
}
window.customElements.define('my-elem', MyElem);
<template id="template-elem">
<div class="a">
<div class="b">b</div>
<div class="c">c</div>
</div>
</template>
<my-elem></my-elem>
为什么这不起作用?在 Chrome 检查器中,自定义元素内部没有 HTML。我也尝试过这样做:
this.append(templateContent.cloneNode(true));
但这也导致了一个空的 HTML 树。
所有教程都提到使用 Shadow DOM,如下所示:
this.attachShadow({mode: 'open'}).appendChild(templateContent.cloneNode(true));
虽然这有效,但它会强制您为自定义元素使用 Shadow DOM。有没有办法将模板的 HTML 附加到自定义元素而不需要使用 Shadow DOM?我更愿意在我的小用例中使用全局 CSS 样式。
你正陷入多个陷阱,就像每个人在第一次组件冒险中一样。
自定义元素(严格来说,只有 withshadowDOM 元素才是 Web 组件)具有生命周期阶段和回调。
此图:https://andyogo.github.io/custom-element-reactions-diagram/是必须理解的。
你想在
constructor
阶段添加 DOM 内容;但此阶段还没有 DOM 元素。connectedCallback
中才能添加DOM内容。constructor
中可用,你可以设置内容,但它还不是一个DOM元素!
connectedCallback
会告诉您自定义元素何时附加到 DOM。
.innerHTML
需要一个字符串。由于(在您的用法中)
<template>
是一个DOM元素,您可以读取它的innerHTML(见下文)
是自定义元素没有shadowDOM是可能的:
您将看到<template>
内容两次,演示了添加内容的两种方式。
<script>
customElements.define("my-element", class extends HTMLElement {
connectedCallback() {
let template = document.getElementById(this.nodeName);
this.innerHTML = template.innerHTML;
this.append(template.content.cloneNode(true))
}
})
</script>
<template id="MY-ELEMENT">
Hello, I am an Element!
</template>
<!-- the TEMPLATE MUST exist in the DOM for <my-element> to use it! -->
<my-element></my-element>
constructor
是您准备您的元素的地方 当您执行
constructor
时,此
document.createElement("my-element")
也会运行。当您的元素添加到 DOM 时,
connectedCallback
就会运行如果不指定方法,则运行其父类中的方法,因此在上面的代码中,执行 HTMLElement 中的(默认)
constructor
。这就是为什么您需要在自己的
super()
中使用
constructor
...从 HTMLElement 执行
constructor
。注:
constructor(){
let template = document.getElementById("MY-ELEMENT").content.cloneNode(true);
super().attachShadow({mode:"open").append(template);
}
是完全有效的代码;谷歌文档说 “super 需要先运行” 是错误的。
您需要运行 super()
before 您可以使用
this
访问Elements 自己的范围 这就是为什么我更喜欢:
constructor(){
// do anything you want here, but you can not use 'this'
super() // Sets AND Returns 'this'
.attachShadow({mode:"open") // both Sets AND Returns this.shadowRoot
.innerHTML = ` ... `;
}
或
constructor(){
// do anything you want here, but you can not use 'this'
super() // Sets AND Returns 'this'
.attachShadow({mode:"open") // both Sets AND Returns this.shadowRoot
.append(document.getElementById(this.nodeName).content.cloneNode(true));
}
注意 append() 在 IE 中不可用;所以 oldskool 程序员不会知道它的多功能性:https://developer.mozilla.org/en-US/docs/Web/API/Element/append
当您的组件冒险将涉及类继承时;
您可以使用以下方式调用父方法:
connectedCallback(){
super.connectedCallback()
}
class MyComponent extends HTMLElement {
connectedCallback() {
this.innerHTML = `<div>Hello world</div>`
}
}
customElements.define('my-component', MyComponent)
my-component {
display: block;
border: 1px dotted #900
}
<my-component></my-component>
但是,如果不使用 Shadow DOM,则无法封装 CSS,而必须通过外部样式表来设置组件的样式。
使用 Shadow DOM 编写组件的最简单方法如下所示:
class MyOtherComponent extends HTMLElement {
constructor() {
super()
this.shadow = this.attachShadow({ mode: "open" })
}
connectedCallback() {
this.shadow.innerHTML = `
<style>
:host {
display: block;
border: 1px dotted #900
}
</style>
<div class="component">Hello World!</div>
`
}
}
customElements.define('my-other-component', MyOtherComponent)
<my-other-component></my-other-component>