未能构建 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__headercard__body 的元素,并且您正在尝试访问它,这就是为什么 this.headerNodeundefined.

自定义元素的构造函数中不允许进行某些操作。有关此检查的更多信息 ).

其中包括:

  • 访问属性(尤其是写属性,这包括 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,任何 insideafter 你的 <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';
  }