Я изучаю веб-компоненты и создаю динамический список, чтобы освоить их. Заработав, прочитал, что теневой корень лучше всего прикреплять методом connectedCallback. Однако, попытавшись это сделать, я получаю кучу ошибок, которые не могу исправить.
Кроме того, способ, которым я устанавливаю простой атрибут для метки, кажется немного затянутым: есть ли более простой способ просто выбрать атрибут и отобразить его как метку?
Это мой рабочий пример:
const template = document.createElement('template');
template.innerHTML = `
<style>
:host {
display: block;
font-family: sans-serif;
text-align: center;
}
button {
border: none;
cursor: pointer;
}
ul {
list-style: none;
padding: 0;
}
</style>
<h1>To dos</h1>
<lable id = "lable1"></lable>
<select></select>
`;
class TodoApp extends HTMLElement {
constructor() {
super();
this._shadowRoot = this.attachShadow({ 'mode': 'open' });
this._shadowRoot.appendChild(template.content.cloneNode(true));
this.$todoList = this._shadowRoot.querySelector('select');
this.label1 = this._shadowRoot.getElementById('lable1')
}
static get observedAttributes() {
return ['att1'];
}
attributeChangedCallback(name, oldValue, newValue) {
this.label1.innerText = this.getAttribute('att1');
}
renderTodoList() {
this.$todoList.innerHTML = '';
this.todosArray.forEach((todoP) => {
let $todoItem = document.createElement('option');
$todoItem.text = todoP.text;
$todoItem.value = todoP.id;
this.$todoList.appendChild($todoItem);
});
}
set todos(value) {
this.todosArray = value;
this.renderTodoList();
}
}
window.customElements.define('to-do-app', TodoApp);
Когда я добавляю метод connectedCallback() и создаю там теневой дом, я получаю кучу ошибок.
Моя разметка просто:
<to-do-app att1 = "value 1 attribute"></to-do-app>
Я пробовал это:
class TodoApp extends HTMLElement {
constructor() {
super();
this.label1 = '';
}
connectedCallback() {
this._shadowRoot = this.attachShadow({ 'mode': 'open' });
this._shadowRoot.appendChild(template.content.cloneNode(true));
this.$todoList = this._shadowRoot.querySelector('select');
this.label1 = this._shadowRoot.getElementById('lable1')
}
static get observedAttributes() {
return ['att1'];
}
attributeChangedCallback(name, oldValue, newValue) {
this.label1.innerText = this.getAttribute('att1');
}
Но получите ошибку:
TypeError: can't assign to property "innerText" on "": not an object



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Я совсем не уверен, что лучше всего определить Shadow DOM в connectedCallback() (если вы не хотите работать с полифиллом Shadow DOM. Где вы это прочитали?
В любом случае, если ваш пример с connectedCallback(), ошибка связана с тем, что attributeChangedCallback() вызывается перед connectedCallback().
Вот почему свойство this.label1 еще не установлено, когда вызывается attributeChangeCallback().
Вместо этого проверьте существование свойства:
attributeChangedCallback(name, oldValue, newValue) {
if ( this.label1 )
this.label1.innerText = this.getAttribute('att1');
}
И при рендеринге компонента проверьте наличие атрибута:
connectedCallback() {
//...
this.label1 = this._shadowRoot.getElementById('lable1')
if ( this.getAttribute( 'att1' ) )
this.label1.innerText = this.getAttribute( 'att1' )
}
Обновлять
Лучший способ чтения атрибута зависит от того, когда он вам нужен. В вашем случае использования, поскольку он уже находится в разметке, когда он вам нужен в connectedCallback(), вы можете просто получить его, используя this.getAttribute().
Чтобы присвоить его значение, возможно, вам следует использовать литерал шаблона с переменной вместо элемента <template>.
let label = this.getAttribute( 'att1' )
this.shadowRoot.innerHTML = `<label>${label}</label>`
Есть идеи по моему второму вопросу: как проще всего прочитать атрибут в разметке и назначить его текстовой метке. Он не динамический, и его нужно установить только один раз при отображении страницы. То, как я заработал, кажется немного OTT для того, что я думал, будет простой операцией. Спасибо за помощь выше!
Я прочитал совет здесь: dev.to/bennypowers/…