通过createElement创建WebComponent

Zer*_*Ten 10 javascript web-component

我在使用 createElement 创建 Web 组件时遇到问题。我收到此错误:

未捕获的 DOMException:无法构造“CustomElement”:结果在appendTodo 中不能有子级

class TodoCard extends HTMLElement {
    constructor() {
        super()

        this.innerHTML = `
            <li>
                <div class="card">
                    <span class="card-content">${this.getAttribute('content')}</span>
                    <i class="fa fa-circle-o" aria-hidden="true"></i>
                    <i class="fa fa-star-o" aria-hidden="true"></i>
                </div>
            </li>
        `
    }
}

window.customElements.define('todo-card', TodoCard)

const todoList = document.getElementById('todo-list')
const todoForm = document.getElementById('todo-form')
const todoInput = document.getElementById('todo-input')

function appendTodo(content) {
    const todo = document.createElement('todo-card')
    todo.setAttribute('content', content)
    todoList.appendChild(todo)
}

todoForm.addEventListener('submit', e => {
    e.preventDefault()
    appendTodo(todoInput.value)
    todoInput.value = ''
})
Run Code Online (Sandbox Code Playgroud)

有任何想法吗?谢谢。

Dan*_*man 14

constructor
永远无法创建在 中设置 DOM 内容的自定义元素document.createElement()

您将看到许多示例(包括我的示例),其中 DOM 内容是在构造函数中设置的。
这些元素永远无法创建document.createElement

说明(HTML DOM API):

当您使用:

  <todo-card content=FOO></todo-card>
Run Code Online (Sandbox Code Playgroud)

元素(从 HTMLElement 扩展)具有所有 HTML 接口(它位于 HTML DOM 中),
并且您可以在构造函数中设置innerHTML

但是,当你这样做时:

  document.createElement("todo-card");
Run Code Online (Sandbox Code Playgroud)

构造函数在没有 HTML 接口的情况下运行(该元素可能与 DOM 无关),
因此在构造函数中设置innerHTML会产生错误:

未捕获的 DOMException:无法构造“CustomElement”:结果不得有子级

来自https://html.spec.whatwg.org/multipage/custom-elements.html#custom-element-conformance

该元素不得获得任何属性或子元素,因为这违反了使用 createElement 或 createElementNS 方法的使用者的期望。一般来说,工作应该尽可能推迟到connectedCallback

ShadowDOM 是一个 DOM

使用shadowDOM时,您可以在构造函数中设置shadowDOM内容:

  constructor(){
    super().attachShadow({mode:"open"})
           .innerHTML = `...`;
  }
Run Code Online (Sandbox Code Playgroud)

正确的代码(没有shadowDOM):使用connectedCallback

  <todo-card content=FOO></todo-card>
Run Code Online (Sandbox Code Playgroud)

您还有另一个小问题:content 默认属性,FireFox 不会停止警告您:

或者不使用 createElement

  const todo = document.createElement("todo-card");
  todo.setAttribute("content", "BAR");
  document.body.appendChild(todo);
Run Code Online (Sandbox Code Playgroud)

可以写成:

  const html = `<todo-card content="BAR"></todo-card`;
  document.body.insertAdjacentHTML("beforeend" , html); 
Run Code Online (Sandbox Code Playgroud)

可以connectedCallback运行多次!

当您移动 DOM 节点时:

  document.createElement("todo-card");
Run Code Online (Sandbox Code Playgroud)

  • connectCallback 为 LIT 运行
  • 当 LIT 移动时
  • disconnectedCallback 运行(注意父元素!元素已经位于新位置)
  • LIT 的connectedCallback 再次运行,"Learn Lit" 再次追加

您的组件/应用程序必须如何处理这个问题取决于程序员

Web 组件库

像 Lit、HyperHTML 和 Hybrids 这样的库实现了额外的回调来帮助完成这一切。

我建议先学习自定义元素 API,否则你学习的是工具而不是技术。

有了工具的傻瓜仍然是傻瓜