template, slots y custom elements en HTML | Nicolás Garzón
template
slot
HTML
Copiar < template id = " project-card-template" >
< article>
< h2> </ h2>
< p> </ p>
</ article>
</ template> El contenido de template se parsea, pero no se renderiza ni ejecuta como parte activa del documento.
HTML
Copiar < template id = " message-template" >
< p class = " message" > </ p>
</ template> JavaScript accede a un DocumentFragment mediante .content:
JavaScript
Copiar const template = document. querySelector ( "#message-template" ) ;
const fragment = template. content. cloneNode ( true ) ; Scripts, imágenes y handlers dentro de un template no se activan hasta insertarse, pero el contenido puede volverse peligroso al clonarse.
No construyas templates mediante HTML no confiable sin sanitización.
HTML
Copiar < template>
< label for = " name" > Nombre</ label>
< input id = " name" >
</ template> Clonar varias veces duplica IDs y rompe relaciones. Genera IDs únicos o usa una asociación que se ajuste al componente.
HTML
Copiar < article-card>
< template shadowrootmode = " open" >
< style> ... </ style>
< slot name = " title" > </ slot>
< slot> </ slot>
</ template>
< h2 slot = " title" > DomiSys</ h2>
< p> Sistema de gestión.</ p>
</ article-card> Permite declarar un shadow root desde HTML en navegadores compatibles, útil para renderizado del servidor e hidratación.
Verifica soporte y estrategia de fallback.
HTML
Copiar < project-card> </ project-card> El nombre debe contener un guion para distinguirlo de elementos estándar y reservar compatibilidad futura.
No uses nombres que podrían confundirse con elementos built-in.
JavaScript
Copiar class ProjectCard extends HTMLElement {
connectedCallback ( ) {
}
}
customElements. define ( "project-card" , ProjectCard) ; La definición mejora elementos existentes en el documento y futuros.
Un custom element puede aparecer antes de definirse:
HTML
Copiar < project-card> </ project-card>
< script type = " module" src = " project-card.js" > </ script> Inicialmente es un elemento desconocido ordinario; al definirse se actualiza. Diseña estilos y contenido para evitar una experiencia rota durante el upgrade.
:defined permite detectar estado:
CSS
Copiar project-card:not(:defined) { visibility : hidden; } Ocultarlo puede causar layout shift o contenido ausente. Prefiere una base visible y progresiva cuando sea posible.
Texto
Copiar constructor
connectedCallback
disconnectedCallback
adoptedCallback
attributeChangedCallbackEl constructor debe hacer trabajo mínimo. El elemento puede conectarse y desconectarse varias veces; cleanup y listeners deben ser seguros.
JavaScript
Copiar static observedAttributes = [ "status" ] ;
attributeChangedCallback ( name, oldValue, newValue ) {
} Los atributos son strings o ausencia. Propiedades JavaScript pueden representar datos complejos.
Define claramente reflection entre properties y attributes para evitar loops.
JavaScript
Copiar this . attachShadow ( { mode : "open" } ) ; Encapsula nodos y estilos bajo reglas específicas. No es una frontera de seguridad: scripts de la página pueden interactuar con el host y un shadow root open.
El contenido accesible puede cruzar composición, pero debes probar nombres, labels y relaciones entre light/shadow DOM.
HTML
Copiar < slot name = " title" > </ slot>
< slot> </ slot> HTML
Copiar < project-card>
< h2 slot = " title" > DomiSys</ h2>
< p> Descripción.</ p>
</ project-card> El contenido sigue perteneciendo al light DOM, pero se presenta en el punto de inserción del slot.
HTML
Copiar < slot name = " title" > Proyecto sin título</ slot> Se muestra si no existe contenido asignado.
JavaScript
Copiar slot. addEventListener ( "slotchange" , ( ) => {
} ) ; No necesariamente detecta cada mutación interna del nodo asignado; detecta cambios en asignación.
HTML
Copiar < project-card role = " article" > ...</ project-card> No añadas un role para simular estructura si puedes colocar un <article> real dentro o usar un custom element que extienda un patrón probado.
Un nombre de custom element no tiene semántica nativa por sí mismo.
JavaScript
Copiar class FancyButton extends HTMLButtonElement { }
customElements. define ( "fancy-button" , FancyButton, {
extends : "button" ,
} ) ; HTML
Copiar < button is = " fancy-button" > ...</ button> El soporte e interoperabilidad han sido históricamente desiguales. Los autonomous custom elements son más comunes, pero deben reconstruir cuidadosamente semántica cuando actúan como controles.
JavaScript
Copiar class RatingInput extends HTMLElement {
static formAssociated = true ;
#internals = this . attachInternals ( ) ;
} ElementInternals permite participar en forms, labels, validity y accessibility semantics.
Implementar un control completo requiere:
Name/value.
Disabled/read-only policy.
Reset.
Validation.
Keyboard.
Focus.
Accessible name.
No crees un custom input si un control nativo cubre el caso.
ElementInternals puede exponer estados para CSS en implementaciones modernas. Son una capacidad avanzada y dependiente del soporte objetivo.
Declarative Shadow DOM puede entregar contenido desde servidor. La definición posterior debe adoptar o reutilizar el shadow root, no duplicarlo.
Sin JavaScript.
Antes del upgrade.
Después de hydration.
Con errores de carga.
HTML
Copiar < project-card>
< h2 slot = " title" >
< a href = " /portfolio/domisys" > DomiSys</ a>
</ h2>
< p> Sistema de gestión para negocios.</ p>
</ project-card> El contenido importante permanece en light DOM y funciona aunque la definición falle.
Creer que template sanitiza contenido.
Clonar IDs duplicados.
Ocultar custom elements hasta defined y causar vacío/layout shift.
Hacer trabajo irreversible en connectedCallback.
No limpiar listeners.
Tratar attributes como objetos.
Confundir Shadow DOM con seguridad.
Romper labels al cruzar boundaries.
Crear controles custom cuando existe uno nativo.
Asumir soporte uniforme de customized built-ins.
Duplicar Shadow DOM durante hydration.
Template contiene un fragmento inerte clonable.
Custom elements necesitan un nombre con guion.
El upgrade puede ocurrir después del parsing.
Lifecycle callbacks pueden ejecutarse varias veces.
Attributes y properties tienen contratos distintos.
Shadow DOM encapsula, pero no protege secretos.
Slots distribuyen light DOM.
Un custom element no tiene semántica automática.
Los controles form-associated requieren un contrato amplio.
Diseña una base progresiva antes del upgrade.
¿Por qué clonar un template con id="email" varias veces puede romper labels?
Respuesta Porque los IDs dejan de ser únicos y cada for="email" puede asociarse de forma ambigua o con el primer elemento encontrado.
Metadata para SEO, social y datos estructurados organiza cómo otras plataformas descubren y representan el documento.