如何在没有 attachShadow 的情况下创建自定义元素?
How to create a custom element without attachShadow?
假设我有这样的代码:
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 树。
所有教程都提到使用阴影 DOM,如下所示:
this.attachShadow({mode: 'open'}).appendChild(templateContent.cloneNode(true));
虽然这行得通,但它会强制您对自定义元素使用阴影 DOM。有没有办法只将模板的 HTML 附加到您的自定义元素而不需要使用 Shadow DOM?我更愿意在我的小用例中使用全局 CSS 样式。
你正掉入多个陷阱,就像每个人在他们的第一次组件冒险中一样。
自定义元素(严格来说只有元素 with shadowDOM 是 Web 组件)具有生命周期阶段和回调。
此图:https://andyogo.github.io/custom-element-reactions-diagram/ 是 必须 才能理解。
你想在constructor
阶段添加DOM内容;但此阶段还没有 DOM 个元素。
只有在connectedCallback
中才能添加DOM内容。
有了 shadowDOM这是另外一回事了,它的“DocumentFragment”在constructor
中可用,可以设置内容,但是不行一个DOM元素呢! connectedCallback
告诉您自定义元素何时附加到 DOM。
模板内容是一个 DocumentFragment,但您的 .innerHTML
需要一个字符串。
由于(在你的用法中)<template>
是 一个 DOM 元素,你可以阅读它的 innerHTML(见下文)
所以,是自定义元素没有阴影DOM是可能的:
您将看到 <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>
<my-element></my-element>
constructor
是您准备 元素的地方
当您执行 document.createElement("my-element")
时,此 constructor
也会 运行。
connectedCallback
运行 当你的元素被添加到 DOM
如果您没有指定方法,则该方法来自其 Class 父级 运行s,因此在上面的代码中,执行来自 HTMLElement 的(默认)constructor
。
这就是为什么您需要 super()
在您自己的 constructor
... 中执行来自 HTMLElement 的 constructor
。
注:
constructor(){
let template = document.getElementById("MY-ELEMENT").content.cloneNode(true);
super().attachShadow({mode:"open").append(template);
}
是完全有效的代码; Google 说 “super needs to 运行 first” 的文档是错误的。
你需要 运行 super()
before 你可以 access Elements 自己的范围 this
这就是我喜欢的原因:
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() was not available in IE; so oldskool programmers won't know about its versatility: https://developer.mozilla.org/en-US/docs/Web/API/Element/append
当您的组件冒险将涉及 Class 继承时;
你调用父方法:
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,而必须通过外部样式 sheet.
用 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>
这种方式,你有更多的开销,但组件是真正封装的。
假设我有这样的代码:
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 树。
所有教程都提到使用阴影 DOM,如下所示:
this.attachShadow({mode: 'open'}).appendChild(templateContent.cloneNode(true));
虽然这行得通,但它会强制您对自定义元素使用阴影 DOM。有没有办法只将模板的 HTML 附加到您的自定义元素而不需要使用 Shadow DOM?我更愿意在我的小用例中使用全局 CSS 样式。
你正掉入多个陷阱,就像每个人在他们的第一次组件冒险中一样。
自定义元素(严格来说只有元素 with shadowDOM 是 Web 组件)具有生命周期阶段和回调。
此图:https://andyogo.github.io/custom-element-reactions-diagram/ 是 必须 才能理解。
你想在constructor
阶段添加DOM内容;但此阶段还没有 DOM 个元素。
只有在connectedCallback
中才能添加DOM内容。
有了 shadowDOM这是另外一回事了,它的“DocumentFragment”在constructor
中可用,可以设置内容,但是不行一个DOM元素呢!connectedCallback
告诉您自定义元素何时附加到 DOM。模板内容是一个 DocumentFragment,但您的
.innerHTML
需要一个字符串。
由于(在你的用法中)<template>
是 一个 DOM 元素,你可以阅读它的 innerHTML(见下文)
所以,是自定义元素没有阴影DOM是可能的:
您将看到 <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>
<my-element></my-element>
constructor
是您准备 元素的地方
当您执行 document.createElement("my-element")
时,此 constructor
也会 运行。
connectedCallback
运行 当你的元素被添加到 DOM
如果您没有指定方法,则该方法来自其 Class 父级 运行s,因此在上面的代码中,执行来自 HTMLElement 的(默认)constructor
。
这就是为什么您需要 super()
在您自己的 constructor
... 中执行来自 HTMLElement 的 constructor
。
注:
constructor(){
let template = document.getElementById("MY-ELEMENT").content.cloneNode(true);
super().attachShadow({mode:"open").append(template);
}
是完全有效的代码; Google 说 “super needs to 运行 first” 的文档是错误的。
你需要 运行 super()
before 你可以 access Elements 自己的范围 this
这就是我喜欢的原因:
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() was not available in IE; so oldskool programmers won't know about its versatility: https://developer.mozilla.org/en-US/docs/Web/API/Element/append
当您的组件冒险将涉及 Class 继承时;
你调用父方法:
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,而必须通过外部样式 sheet.
用 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>
这种方式,你有更多的开销,但组件是真正封装的。