J'ai un élément personnalisé défini comme suit:
class SquareLetter extends HTMLElement {
constructor() {
super();
this.className = getRandomColor();
}
}
customElements.define("square-letter", SquareLetter);
Lorsque le fichier JavaScript est inclus dans HTML <head>
tag, la console Chrome signale cette erreur:
DOMException non capturée: Échec de la construction de "CustomElement": le résultat ne doit pas avoir d'attributs
Mais lorsque le fichier JavaScript est inclus avant le </body>
balise de fin, tout fonctionne bien. Quelle est la raison?
<head>
<script src="js/SquareLetter.js"></script> <!-- here -->
</head>
<body>
<square-letter>A</square-letter>
<script src="js/SquareLetter.js"></script> <!-- or here -->
</body>
L'erreur est correcte et peut se produire dans les deux cas. Vous avez de la chance car certaines implémentations actuelles des éléments personnalisés n'appliquent pas cette exigence.
Le constructeur d'un élément personnalisé n'est pas censé lire ou écrire son DOM. Il ne doit pas créer d'éléments enfants ni modifier d'attributs. Ce travail doit être effectué plus tard, généralement dans une méthode connectedCallback()
(bien que notez que connectedCallback()
peut être appelé plusieurs fois si l'élément est supprimé et rajouté au DOM, donc vous Il peut être nécessaire de vérifier cela ou d'annuler les modifications d'une disconnectedCallback()
).
Citant la spécification HTML WHATWG, accentuation la mienne:
§ 4.13.2 Exigences pour les constructeurs d'éléments personnalisés :
Lors de la création de constructeurs d'éléments personnalisés, les auteurs sont liés par les exigences de conformité suivantes:
Un appel sans paramètre à
super()
doit être la première instruction dans le corps du constructeur, pour établir la chaîne de prototype correcte et la valeur this avant d'exécuter un autre code.Une instruction return ne doit apparaître nulle part à l'intérieur du corps du constructeur, sauf s'il s'agit d'un simple retour anticipé (return ou return this).
Le constructeur ne doit pas utiliser les méthodes
document.write()
oudocument.open()
.Les attributs et les enfants de l'élément ne doivent pas être inspectés, car dans le cas sans mise à niveau, aucun ne sera présent, et s'appuyer sur des mises à niveau rend l'élément moins utilisable.
L'élément ne doit pas gagner d'attributs ou d'enfants, car cela viole les attentes des consommateurs qui utilisent les méthodes
createElement
oucreateElementNS
.En général, le travail doit être reporté à
connectedCallback
autant que possible, en particulier le travail impliquant la récupération de ressources ou le rendu. Cependant, notez queconnectedCallback
peut être appelé plus d'une fois, donc tout travail d'initialisation véritablement ponctuel aura besoin d'un garde pour l'empêcher de s'exécuter deux fois.En général, le constructeur doit être utilisé pour configurer l'état initial et les valeurs par défaut, et pour configurer les écouteurs d'événements et éventuellement une racine fantôme.
Plusieurs de ces exigences sont vérifiées lors de la création de l'élément, directement ou indirectement, et si elles ne sont pas respectées, un élément personnalisé ne peut pas être instancié par l'analyseur ou les API DOM. Cela est vrai même si le travail est effectué à l'intérieur d'une microtâche initiée par le constructeur, car un point de contrôle de microtâche peut se produire immédiatement après la construction.
Lorsque vous déplacez le script après l'élément dans le DOM, vous faites passer les éléments existants par le processus de "mise à niveau". Lorsque le script est avant l'élément, l'élément passe par le processus de construction standard. Cette différence fait que l'erreur n'apparaît pas dans tous les cas, mais c'est un détail d'implémentation et peut changer.
Je fais face au même problème lorsque CreatElement avec CustomComponent. Il n'a pas été corrigé tant que je n'ai pas tout supprimé du constructeur, sauf la super fonction de la fonction connectedCallback.