未能构建 CustomElement
Failed to construct CustomElement
我在使用自定义元素时遇到问题。
错误: Uncaught DOMException: Failed to construct 'CustomElement': The result must not have children
'use strict';
class TestCard extends HTMLDivElement {
constructor() {
super();
this.headerNode = document.createElement('div');
this.bodyNode = document.createElement('div');
this.headerNode.className = 'card__header';
this.bodyNode.className = 'card__body';
this.appendChild(this.headerNode);
this.appendChild(this.bodyNode);
}
connectedCallback() {
this.classList.add('card');
}
static get observedAttributes() {
return ['data-header', 'data-body'];
}
attributeChangedCallback(attrName, oldValue, newValue) {
if (newValue !== oldValue) {
this[attrName.replace('data-', '')] = newValue;
}
}
set header(value) {
this.headerNode.textContent = value;
this.setAttribute('data-header', value);
}
set body(value) {
this.bodyNode.innerHTML = value;
this.setAttribute('data-body', value);
}
}
customElements.define('test-card', TestCard, {
extends: 'div'
});
<div is="test-card" data-header="Title" data-body="Content"></div>
创建 WebComponent :
var cardNode = document.createElement('div');
cardNode.setAttribute('is', 'test-card');
cardNode.header = header;
cardNode.body = body;
我不能说你提到的错误,但还有一件事你应该改变。
在你的代码中重新排序这些东西来自
constructor() {
super();
this.classList.add('card');
this.headerNode = this.getElementsByClassName('card__header')[0];
this.bodyNode = this.getElementsByClassName('card__body')[0];
this.innerHTML = `<div class="card__header"></div>
<div class="card__body"></div>`;
}
到
constructor() {
super();
this.classList.add('card');
this.innerHTML = `<div class="card__header"></div>
<div class="card__body"></div>`;
this.headerNode = this.getElementsByClassName('card__header')[0];
this.bodyNode = this.getElementsByClassName('card__body')[0];
}
由于您稍后在代码中添加了带有 类 card__header
和 card__body
的元素,并且您正在尝试访问它,这就是为什么 this.headerNode
是 undefined
.
自定义元素的构造函数中不允许进行某些操作。有关此检查的更多信息 ).
其中包括:
- 访问属性(尤其是写属性,这包括
class
被认为在使用您的组件的人的控制之下)
- 正在访问 children(既不读也不写)
- 除非你在组件的影子树中这样做。
要实现你想做的事情,使用影子DOM:
class TestComp extends HTMLElement {
headerNode = document.createElement('div');
bodyNode = document.createElement('div');
constructor() {
super();
this.attachShadow({mode: 'open'});
this.headerNode.className = 'card__header';
this.bodyNode.className = 'card__body';
this.bodyNode.part = 'body';
this.shadowRoot.append(this.headerNode, this.bodyNode);
}
connectedCallback() {
this.classList.add('card');
}
static get observedAttributes() {
return ['data-header', 'data-body'];
}
attributeChangedCallback(attrName, oldValue, newValue) {
if (newValue !== oldValue) {
this[attrName.replace('data-', '')] = newValue;
}
}
set header(value) {
this.headerNode.textContent = value;
this.dataset.header = value;
}
set body(value) {
this.bodyNode.innerHTML = value;
this.dataset.body = value;
}
}
customElements.define('test-comp', TestComp);
let newTestComp = new TestComp();
newTestComp.header = 'FOOO';
newTestComp.body = '<ul><li><i>BA</i><b>AA</b>R</ul>';
document.body.append(newTestComp);
test-comp::part(body) { color: green; }
<test-comp data-header="Titre de ma carte" data-body="<h1>Test</h1>"></test-comp>
请注意,使用阴影 DOM 意味着外部样式不会影响阴影树中元素的样式。要将样式应用于这些样式,请在构造函数中创建一个 <style>
元素,将其设置为 textContent
属性 您的样式,并将其附加到阴影 DOM 中的其他元素旁边.
除了使用 style
元素,您还可以使用 Constructable Stylesheets. You'll probably need a polyfill because so far Chromium-based browsers are the only ones supporting it, but support is coming in other browsers(Firefox 已经在标志后面使用了一段时间:打开新选项卡,导航到 about:config
,然后将 layout.css.constructable-stylesheets.enabled
设置为 true
).
要允许从外部设置组件内部的样式 CSS,您可以使用影子 DOM 中的 part="name"
属性指定允许从外部设置样式的元素,然后使用 CSS 中的 ::part(name)
选择器设置样式。将其添加到代码示例中。
正如我在 Connexo 中评论他的旧答案。
您必须了解 Web 组件 生命周期:https://andyogo.github.io/custom-element-reactions-diagram/
当你 运行 new TestComp()
或 document.createElement("test-comp")
没有 DOM 元素,只有 constructor
运行 元素,并告诉你,
你不能 add DOM (children) 到它 (nothing)。只有在 connectedCallback
中,您才能使用 existing DOM,任何 inside 或 after 你的 <test-comp>
可能 还不存在;参见:
并请优化该构造函数,
任何在 constructor
中显示 this.shadowRoot
的博客或答案都应该被烧毁
constructor() {
super() // sets AND returns "this" scope
.attachShadow({mode:'open'}) // sets AND returns shadowRoot
.append(
this.headerNode = document.createElement('div'),
this.bodyNode = document.createElement('div')
);
this.headerNode.className = 'card__header';
this.bodyNode.className = 'card__body';
}
我在使用自定义元素时遇到问题。
错误: Uncaught DOMException: Failed to construct 'CustomElement': The result must not have children
'use strict';
class TestCard extends HTMLDivElement {
constructor() {
super();
this.headerNode = document.createElement('div');
this.bodyNode = document.createElement('div');
this.headerNode.className = 'card__header';
this.bodyNode.className = 'card__body';
this.appendChild(this.headerNode);
this.appendChild(this.bodyNode);
}
connectedCallback() {
this.classList.add('card');
}
static get observedAttributes() {
return ['data-header', 'data-body'];
}
attributeChangedCallback(attrName, oldValue, newValue) {
if (newValue !== oldValue) {
this[attrName.replace('data-', '')] = newValue;
}
}
set header(value) {
this.headerNode.textContent = value;
this.setAttribute('data-header', value);
}
set body(value) {
this.bodyNode.innerHTML = value;
this.setAttribute('data-body', value);
}
}
customElements.define('test-card', TestCard, {
extends: 'div'
});
<div is="test-card" data-header="Title" data-body="Content"></div>
创建 WebComponent :
var cardNode = document.createElement('div');
cardNode.setAttribute('is', 'test-card');
cardNode.header = header;
cardNode.body = body;
我不能说你提到的错误,但还有一件事你应该改变。
在你的代码中重新排序这些东西来自
constructor() {
super();
this.classList.add('card');
this.headerNode = this.getElementsByClassName('card__header')[0];
this.bodyNode = this.getElementsByClassName('card__body')[0];
this.innerHTML = `<div class="card__header"></div>
<div class="card__body"></div>`;
}
到
constructor() {
super();
this.classList.add('card');
this.innerHTML = `<div class="card__header"></div>
<div class="card__body"></div>`;
this.headerNode = this.getElementsByClassName('card__header')[0];
this.bodyNode = this.getElementsByClassName('card__body')[0];
}
由于您稍后在代码中添加了带有 类 card__header
和 card__body
的元素,并且您正在尝试访问它,这就是为什么 this.headerNode
是 undefined
.
自定义元素的构造函数中不允许进行某些操作。有关此检查的更多信息
其中包括:
- 访问属性(尤其是写属性,这包括
class
被认为在使用您的组件的人的控制之下) - 正在访问 children(既不读也不写)
- 除非你在组件的影子树中这样做。
要实现你想做的事情,使用影子DOM:
class TestComp extends HTMLElement {
headerNode = document.createElement('div');
bodyNode = document.createElement('div');
constructor() {
super();
this.attachShadow({mode: 'open'});
this.headerNode.className = 'card__header';
this.bodyNode.className = 'card__body';
this.bodyNode.part = 'body';
this.shadowRoot.append(this.headerNode, this.bodyNode);
}
connectedCallback() {
this.classList.add('card');
}
static get observedAttributes() {
return ['data-header', 'data-body'];
}
attributeChangedCallback(attrName, oldValue, newValue) {
if (newValue !== oldValue) {
this[attrName.replace('data-', '')] = newValue;
}
}
set header(value) {
this.headerNode.textContent = value;
this.dataset.header = value;
}
set body(value) {
this.bodyNode.innerHTML = value;
this.dataset.body = value;
}
}
customElements.define('test-comp', TestComp);
let newTestComp = new TestComp();
newTestComp.header = 'FOOO';
newTestComp.body = '<ul><li><i>BA</i><b>AA</b>R</ul>';
document.body.append(newTestComp);
test-comp::part(body) { color: green; }
<test-comp data-header="Titre de ma carte" data-body="<h1>Test</h1>"></test-comp>
请注意,使用阴影 DOM 意味着外部样式不会影响阴影树中元素的样式。要将样式应用于这些样式,请在构造函数中创建一个 <style>
元素,将其设置为 textContent
属性 您的样式,并将其附加到阴影 DOM 中的其他元素旁边.
除了使用 style
元素,您还可以使用 Constructable Stylesheets. You'll probably need a polyfill because so far Chromium-based browsers are the only ones supporting it, but support is coming in other browsers(Firefox 已经在标志后面使用了一段时间:打开新选项卡,导航到 about:config
,然后将 layout.css.constructable-stylesheets.enabled
设置为 true
).
要允许从外部设置组件内部的样式 CSS,您可以使用影子 DOM 中的 part="name"
属性指定允许从外部设置样式的元素,然后使用 CSS 中的 ::part(name)
选择器设置样式。将其添加到代码示例中。
正如我在 Connexo 中评论他的旧答案。
您必须了解 Web 组件 生命周期:https://andyogo.github.io/custom-element-reactions-diagram/
当你 运行 new TestComp()
或 document.createElement("test-comp")
没有 DOM 元素,只有 constructor
运行 元素,并告诉你,
你不能 add DOM (children) 到它 (nothing)。只有在 connectedCallback
中,您才能使用 existing DOM,任何 inside 或 after 你的 <test-comp>
可能 还不存在;参见:
并请优化该构造函数,
任何在 constructor
中显示 this.shadowRoot
的博客或答案都应该被烧毁
constructor() {
super() // sets AND returns "this" scope
.attachShadow({mode:'open'}) // sets AND returns shadowRoot
.append(
this.headerNode = document.createElement('div'),
this.bodyNode = document.createElement('div')
);
this.headerNode.className = 'card__header';
this.bodyNode.className = 'card__body';
}