Пользовательский элемент JS получает внутренний HTML

У нас есть этот пользовательский элемент, определенный так...

<my-button>
   Submit
</my-button>

и стандартные определения customElements

class MyButton extends HTMLElement{
   constructor(){
      super();
      // our custom code...
      this.innerHTML = ''; // ??? where did the 'Submit' go?
   }
}

...

customElements.define('my-button',MyButton);

Проблема в том, что при попытке получить innerHTML мы знаем, что можем сделать что-то вроде DOMContentLoaded или window.onload.

но иногда мы хотели бы создать «мою кнопку» динамично, используя код. и пусть он "рендерится" при добавлении...

Есть ли стандартный способ сделать это? это как-то связано с connectedcallback() и остальными «подключенными» функциями?

Спасибо!

Обратите внимание: я пытался использовать connectedCallback() в качестве возможного решения, и это не решает проблему.

Вот схема жизненного цикла: andyogo.github.io/пользовательский-элемент-реакции-диаграмма

Danny '365CSI' Engelman 18.03.2019 12:39

Вы не можете получить доступ к DOM вашего пользовательского элемента в конструкторе, потому что у него нет DOM, пока он не подключен.

Thad 18.03.2019 15:58
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
4
3
1 617
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Существует набор правил о том, что можно и что нельзя делать в конструкторе веб-компонента. Они ниже.

Но подумайте об этом:

Компонент может быть создан одним из трех способов:

  1. Часть исходной страницы/использование innerHTML: Когда браузер загружает страницу или при использовании innerHTML, вы можете добавлять атрибуты и дочерние элементы к компоненту как часть загрузки страницы или часть innerHTML.
parent.innerHTML = '<super-hero name = "Thor"><super-weapon value = "Mjolnir"></super-weapon></super-hero>'.
  1. Вы можете вызвать document.createELement, чтобы создать элемент. Вы не можете добавлять атрибуты или дочерние элементы до тех пор, пока элемент не будет создан.
let superHero = document.createElement('super-hero');
let superWeapon = document.createElement('super-weapon');
superHero.setAttribute('name', 'Thor');
superWeapon.setAttribute('value',  'Mjolnir');
superHero.appendChild(superWeapon);
parent.appendChild(superHero)
  1. Вы можете создать экземпляр объекта, используя new. Как и document.createElement, вы должны дождаться создания элемента, прежде чем добавлять атрибуты и дочерние элементы.
let superHero = new SuperHero();
let superWeapon = new SuperWeapon();
superHero.setAttribute('name', 'Thor');
superWeapon.setAttribute('value',  'Mjolnir');
superHero.appendChild(superWeapon);
parent.appendChild(superHero)

Как только компонент создан, он добавляется в DOM, и именно тогда вызывается connectedCallback вашего компонента.

И все эти три способа на самом деле сводятся к созданию экземпляров с помощью new. document.createElement вызывает CustomElementRegistry.get, чтобы получить конструктор для этого элемента, а затем использует new для создания объекта.

И innerHTML анализирует HTML, а затем либо вызывает document.createElement, либо использует new для создания объекта.

Но при этом НЕТ атрибутов или дочерних элементов, когда вызывается конструктор для вашего элемента. На самом деле, при вызове connectedCallback может не быть дочерних элементов или атрибутов. Это одна из причин, по которой observedAttributes и attributeChangedCallback были добавлены в спецификацию.

Единственное, чего не хватает в спецификации, это знать, что кто-то добавил или изменил дочерние элементы до или после того, как компонент был добавлен в DOM. Но если вы действительно хотите знать, когда меняются дети, вы можете использовать MutationObserver.

Вот почему в вашем конструкторе нет дочерних элементов или атрибутов. Они еще не добавлены.

Теперь о правилах:

When authoring custom element constructors, authors are bound by the following conformance requirements:

  • A parameter-less call to super() must be the first statement in the constructor body, to establish the correct prototype chain and this value before any further code is run.

  • A return statement must not appear anywhere inside the constructor body, unless it is a simple early-return (return or return this).

  • The constructor must not use the document.write() or document.open() methods.

  • The element's attributes and children must not be inspected, as in the non-upgrade case none will be present, and relying on upgrades makes the element less usable.

  • The element must not gain any attributes or children, as this violates the expectations of consumers who use the createElement or createElementNS methods.

  • In general, work should be deferred to connectedCallback as much as possible—especially work involving fetching resources or rendering. However, note that connectedCallback can be called more than once, so any initialization work that is truly one-time will need a guard to prevent it from running twice.

  • In general, the constructor should be used to set up initial state and default values, and to set up event listeners and possibly a shadow root.

Several of these requirements are checked during element creation, either directly or indirectly, and failing to follow them will result in a custom element that cannot be instantiated by the parser or DOM APIs.

Предыдущая ссылка на правила не работает; новая ссылка: html.spec.whatwg.org/multipage/… (и, кажется, в сообщении есть полная очередь «предлагаемых правок», поэтому я не могу ее обновить)

wtr 10.07.2021 20:10

Есть 2 способа.

  1. Вы можете определить пользовательский элемент после window.onload, и все должно работать нормально.
window.addEventListener('load', () => {
    customElement.define('my-button', MyButton);
});
  1. установить атрибут defer в <script>
<script defer src = "my-button.js"></script>

В 99% случаев ждать load не нужно. Вы почти всегда можете сделать это раньше DOMContentLoaded.

connexo 21.08.2021 12:15

работает отлично, это правильный ответ. Просто чтобы добавить, вы также можете вызвать connectedCallback func, завернутый в window.requestAnimationFrame(func). connectedCallback() { let func = () { ... }; window.requestAnimationFrame(func) }

Dino Reic 30.01.2022 15:40

Если вам нужен надежный доступ к дочерним элементам вашего пользовательского элемента, независимо от того, как сценарий вашего пользовательского элемента размещен в документе, это для вас: https://github.com/WebReflection/html-parsed-элемент

Удивительно сложно определить, когда синтаксический анализатор проанализировал дочерние элементы пользовательского элемента; HTMLParsedElement делает это, многократно проверяя, существует ли nextSibling (что означало бы, что синтаксический анализ текущего элемента выполнен). Текущая реализация заменила эту проверку наблюдателем мутаций, который проверяет childList и subtree изменения в родительском элементе пользовательского элемента.

Другие вопросы по теме