Considere el siguiente escenario donde tengo una lista de elementos personalizados que se crea dinámicamente. CustomElement
tiene una propiedad personalizada displayName
que debería hacer algo cuando se establece:
class CustomElement extends HTMLElement { set displayName(v) { this.querySelector(".name").textContent = v; } } customElements.define("custom-element", CustomElement) const template = document.querySelector("template"); const list = document.querySelector(".list"); document.querySelector("button").addEventListener("click", () => { const customElTemplate = template.content.firstElementChild; console.log("Template constructor: ", customElTemplate.constructor.name); const el = template.content.firstElementChild.cloneNode(true); console.log("Cloned el constructor:", el.constructor.name); el.displayName = "Should has content"; const frag = new DocumentFragment(); frag.appendChild(el); console.log("Cloned el in DocumentFragment constructor:", el.constructor.name); list.appendChild(frag); console.log("Cloned el in Document constructor:", el.constructor.name); });
custom-element { display: block; margin-bottom: .5rem; border: 1px solid black; }
<template> <custom-element><span class="name"></span></custom-element> </template> <button>Add Item</button> <div class="list"></div>
Al hacer clic en el botón, la salida de la consola es:
Template constructor: HTMLElement Cloned el constructor: HTMLElement Cloned el in DocumentFragment constructor: HTMLElement Cloned el in Document constructor: CustomElement
Como puede ver, el elemento clonado no se convierte en CustomElement
hasta que se adjunta al documento raíz y displayName
es simplemente una propiedad simple que no hace nada.
el.displayName = "Should has content";
no funcionará incluso después de adjuntarlo a un DocumentFragment
. Solo funciona después de adjuntarlo al documento (es decir, después de list.appendChild(frag);
en el ejemplo anterior)
¿Por qué está pasando esto? ¿Puedo configurar el elemento de todos modos antes de adjuntarlo al documento principal?
ACTUALIZACIÓN: Veo que hay customElements.upgrade
, pero incluso al agregarlo, nada cambia, a diferencia del ejemplo proporcionado en el artículo:
customElements.upgrade(el);
Descubrí customElements.upgrade
. Resultó que tengo que llamarlo después de adjuntarlo a un DOM de sombra :
frag.appendChild(el); customElements.upgrade(el); // Now el is CustomElement console.log(el.constructor.name);
El método upgrade() de la interfaz CustomElementRegistry actualiza todos los elementos personalizados que contienen sombras en un subárbol de Nodo, incluso antes de que se conecten al documento principal.
Para el ejemplo en mi pregunta, necesito:
Adjuntarlo a un DocumentFragment
,
Llame customElements.upgrade
Ahora puedo usar un comportamiento personalizado como configurar displayName
.
Código de trabajo:
class CustomElement extends HTMLElement { set displayName(v) { this.querySelector(".name").textContent = v; } } customElements.define("custom-element", CustomElement) const template = document.querySelector("template"); const list = document.querySelector(".list"); document.querySelector("button").addEventListener("click", () => { const customElTemplate = template.content.firstElementChild; console.log("Template constructor: ", customElTemplate.constructor.name); const el = template.content.firstElementChild.cloneNode(true); console.log("Cloned el constructor:", el.constructor.name); const frag = new DocumentFragment(); frag.appendChild(el); customElements.upgrade(el); console.log("Cloned el in DocumentFragment constructor:", el.constructor.name); el.displayName = "Should has content"; list.appendChild(frag); console.log("Cloned el in Document constructor:", el.constructor.name); });
custom-element { display: block; margin-bottom: .5rem; border: 1px solid black; }
<template> <custom-element><span class="name"></span></custom-element> </template> <button>Add Item</button> <div class="list"></div>