我在使用 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 = ''
})
有什么想法吗? 谢谢。
在
constructor
document.createElement()
创建
你会看到很多例子(包括我自己的),其中 DOM 内容是在构造函数中设置的。
这些元素永远无法用
document.createElement
创建
使用时:
<todo-card content=FOO></todo-card>
元素(从 HTMLElement 扩展)具有所有 HTML 接口(位于 HTML DOM 中),
你可以在构造函数
但是,当你这样做时:
document.createElement("todo-card");
构造函数运行,没有HTML接口(该元素可能与DOM无关),
因此在构造函数中设置innerHTML会产生错误:
未捕获的 DOMException:无法构造“CustomElement”:结果不能有子元素
来自 https://html.spec.whatwg.org/multipage/custom-elements.html#custom-element-conformance:
该元素不得获得任何属性或子元素,因为这违反了使用 createElement 或 createElementNS 方法的使用者的期望。 一般来说,工作应该尽可能推迟到connectedCallback
使用 shadowDOM 时,您可以在 构造函数中设置 shadowDOM 内容:
constructor(){
super().attachShadow({mode:"open"})
.innerHTML = `...`;
}
connectedCallback
:<todo-card content=FOO></todo-card>
<script>
customElements.define(
"todo-card",
class extends HTMLElement {
constructor() {
super();
//this.innerHTML = this.getAttribute("content");
}
connectedCallback() {
this.innerHTML = this.getAttribute("content");
}
}
);
try {
const todo = document.createElement("todo-card");
todo.setAttribute("content", "BAR");
document.body.appendChild(todo);
} catch (e) {
console.error(e);
}
</script>
您还有另一个小问题:
content
是默认属性,FireFox 不会停止警告您:
const todo = document.createElement("todo-card");
todo.setAttribute("content", "BAR");
document.body.appendChild(todo);
可以写成:
const html = `<todo-card content="BAR"></todo-card`;
document.body.insertAdjacentHTML("beforeend" , html);
connectedCallback
可以运行多次!当你移动 DOM 节点时:
<div id=DO_Learn>
<b>DO Learn: </b><todo-card todo="Custom Elements API"></todo-card>
</div>
<div id="DONT_Learn">
<b>DON'T Learn!!! </b><todo-card todo="React"></todo-card>
</div>
<script>
customElements.define(
"todo-card",
class extends HTMLElement {
connectedCallback() {
let txt = this.getAttribute("todo");
this.append(txt);// and appended again on DOM moves
console.log("qqmp connectedCallback\t", this.parentNode.id, this.innerHTML);
}
disconnectedCallback() {
console.log("disconnectedCallback\t", this.parentNode.id , this.innerHTML);
}
}
);
const LIT = document.createElement("todo-card");
LIT.setAttribute("todo", "Lit");
DO_Learn.append(LIT);
DONT_Learn.append(LIT);
</script>
"Learn Lit"
由程序员决定你的组件/应用程序必须如何处理这个问题
Lit、HyperHTML 和 Hybrids 等库实现了额外的回调,可以帮助解决所有这些问题。
我建议先学习自定义元素API,否则你学习的是工具而不是技术。
傻瓜有了工具,仍然是傻瓜
另请阅读我在
connectedCallback
上发布的 Dev.文章:
https://dev.to/dannyengelman/web-component-developers-do-not-connect-with-the-connectedcallback-yet-4jo7
您在插入 DOM 之前定义自定义元素,您必须遵循以下步骤
下面的代码片段对我很有用
class TodoCard extends HTMLElement {
constructor() {
super()
this.innerHTML = `
<li>
<div class="card">
<span class="card-content">My Component</span>
<i class="fa fa-circle-o" aria-hidden="true"></i>
<i class="fa fa-star-o" aria-hidden="true"></i>
</div>
</li>
`
}
}
const todoList = document.getElementById('todo-list')
function appendTodo() {
const todo = document.createElement('todo-card')
todoList.appendChild(todo)
window.customElements.define('todo-card', TodoCard)
}
appendTodo();
<div id="todo-list"></div>