Каждый раз, когда создается элемент, это делается через конструктор. Но когда вызывается конструктор, нет ни дочерних элементов, ни каких-либо атрибутов. Все они добавляются ПОСЛЕ создания компонента.
Даже если элемент определен на странице HTML, он все равно создается кодом с использованием конструктора, а затем атрибуты и дочерние элементы добавляются кодом, который анализирует DOM на странице HTML.
Когда вызывается конструктор, дочерних элементов нет, и вы не можете добавить их, поскольку анализатор DOM может добавлять их, как только конструктор завершит работу. То же правило применяется к атрибутам.
В настоящее время нет способа указать дочерние элементы shadowDOM или shadowDOM, кроме как посредством кода JS. Парсер DOM не добавит дочерних элементов в shadowDOM.
Таким образом, согласно спецификации, запрещено иметь доступ, изменять или делать что-либо с атрибутами или дочерними элементами в конструкторе. Но, поскольку парсер DOM не может добавить в shadowDOM компоненты что-нибудь недопустимое.
Я справился с этой проблемой, когда не использовал shadowDOM, используя внутренний элемент шаблона, который создается в конструкторе и затем помещается как дочерний элемент после вызова connectedCallback
.
// Class for `<test-el>`
class TestEl extends HTMLElement {
constructor() {
super();
console.log('constructor');
const template = document.createElement('template');
template.innerHTML = '<div class="name"></div>';
this.root = template.content;
this.rendered = false;
}
static get observedAttributes() {
return ['name'];
}
attributeChangedCallback(attrName, oldVal, newVal) {
if (oldVal !== newVal) {
console.log('attributeChangedCallback', newVal);
this.root.querySelector('.name').textContent = newVal;
}
}
connectedCallback() {
console.log('connectedCallback');
if (!this.rendered) {
this.rendered = true;
this.appendChild(this.root);
this.root = this;
}
}
// `name` property
get name() {
return this.getAttribute('name');
}
set name(value) {
console.log('set name', value);
if (value == null) { // Check for null or undefined
this.removeAttribute('name');
}
else {
this.setAttribute('name', value)
}
}
}
// Define our web component
customElements.define('test-el', TestEl);
const moreEl = document.getElementById('more');
const testEl = document.getElementById('test');
setTimeout(() => {
testEl.name = "Mummy";
const el = document.createElement('test-el');
el.name = "Frank N Stein";
moreEl.appendChild(el);
}, 1000);
<test-el id="test" name="Dracula"></test-el>
<hr/>
<div id="more"></div>
Этот код создает шаблон в конструкторе и использует this.root
для ссылки на него.
После вызова connectedCallback
я вставляю шаблон в DOM и изменяю this.root
, чтобы указать this
, чтобы все мои ссылки на элементы все еще работали.
Это быстрый способ позволить вашему компоненту всегда сохранять правильность своих дочерних элементов без использования shadowDOM и только помещать шаблон в DOM как дочерние элементы только после вызова connectedCalback
.