Antes de escribir nuestras propias etiquetas HTML personalizadas, el navegador debe conocer la existencia de ese tipo de etiquetas. Tras crear la clase del Custom Element, debemos escribir una línea de código para asociar el nombre de la etiqueta HTML personalizada con la clase Javascript. Dicho código suele ser algo parecido a esto:
customElements.define("app-element", AppElement);
El elemento customElements
no es más que una referencia al registro de Custom Elements del navegador. Un registro donde se almacenan los tipos de etiquetas HTML personalizadas para que el navegador las reconozca. A través de este registro global, podemos realizar varias tareas relacionadas con los custom elements:
Método | Descripción |
---|---|
.define( name, className) |
Define un custom element en el documento actual. |
.get( name) |
Obtiene la clase de un custom element definido. |
.whenDefined( name) |
Idem, pero resuelve la promesa cuando el custom element es definido. |
.upgrade( node) |
Permite actualizar custom elements manualmente. |
Estos son los métodos disponibles para trabajar con el registro de custom elements. Vamos a verlos uno por uno, detalladamente con un ejemplo de uso.
Quizás, el método más utilizado de los anteriores es customElements.define()
, ya que es un método mediante el cuál se puede establecer una relación entre una etiqueta HTML personalizada (custom element) y una clase con su funcionalidad, que debe extender obligatoriamente de HTMLElement (o de una clase que extienda de ella):
class AppElement extends HTMLElement {
constructor() {
super();
}
}
customElements.define("app-element", AppElement);
No debemos olvidarnos de esta última línea, ya que definir la clase no es suficiente para crear la etiqueta personalizada, hay que asociar la clase con la etiqueta para que el navegador pueda entenderlas y aplicarlas correctamente.
Uncaught DOMException: Failed to execute 'define' on 'CustomElementRegistry': the name "app-element" has already been used with this registry.
Este error parece bastante lógico y nos puede aparecer si intentamos utilizar diferentes clases para un mismo custom element. En el caso de querer dotar de múltiples funcionalidades a un mismo custom element, lo conveniente quizás sería crear una clase que herede de otras y contenga las funcionalidades buscadas, o crear diferentes custom elements.
Uncaught DOMException: Failed to execute 'define' on 'CustomElementRegistry': this constructor has already been used with this registry`.
Si intentamos utilizar una misma clase para varios custom elements diferentes obtendremos este problema. En el caso de querer aplicar la misma clase en varios custom elements diferentes, nos bastaría con crear una nueva clase que extienda de la que queríamos usar, y así resolver dicha limitación.
En la mayoría de los casos, sólo necesitaremos utilizar .define()
para registrar los custom elements de la página actual, pero en alguna situación concreta nos puede interesar acceder a otros custom elements ya registrados de forma manual. Para ello, podemos utilizar .get()
:
class AppElement extends HTMLElement {
/* ... */
}
customElements.define("app-element", AppElement);
customElements.get("app-element") === AppElement; // true
Como se puede ver, con .get()
obtenemos la clase del custom element solicitado.
Por otro lado, .whenDefined()
se podría utilizar para avisarnos cuando un custom element ha sido definido en el registro de nuestra página. Es una versión asíncrona del método .get()
para Custom Elements que aún no han sido definidos. Esto puede ser realmente útil si queremos ejecutar acciones específicas sólo cuando un custom element ha sido inicializado.
Por ejemplo, analicemos el siguiente código:
customElements.whenDefined("app-element").then((data) => {
console.log("AppElement ha sido definido");
});
El console.log()
se ejecutará cuando la página detecte que el custom element app-element
ha sido definido y registrado y está disponible en el registro de la página. En data
se devolverá la clase que implementa el Custom Element.
Por último, tenemos el método .upgrade()
, que se podría utilizar en algunos casos menos frecuentes en los que queramos actualizar un custom element de forma manual.
Normalmente, los custom elements son registrados al inicio de carga de la página y se definen de forma automática. Sin embargo, podría darse el caso en el que hemos creado una etiqueta HTML personalizada sin conectarla al DOM, y antes de haber definido el custom element. En ese caso, el elemento personalizado seguiría siendo una etiqueta HTML y no se habría actualizado aún.
Veámoslo en un ejemplo:
// 1) Creamos en memoria una etiqueta personalizada <app-element>
const element = document.createElement("app-element");
// 2) Posteriormente, definimos el custom element
class AppElement extends HTMLElement { /* ... */ }
customElements.define("app-element", AppElement);
// 3) La etiqueta aún sigue siendo una etiqueta normal
element.constructor === HTMLElement; // true
// 4) La actualizamos manualmente (HTMLElement -> AppElement)
customElements.upgrade(element);
element.constructor === HTMLElement; // false
element.constructor === AppElement; // true
Estas situaciones pueden ocurrir cuando no se hace el customElements.define()
inmediatamente, o en situaciones asíncronas donde se crea primero el elemento antes de tener definido y registrado el Custom Element.
Soy Manz, vivo en Tenerife (España) y soy streamer partner en Twitch y profesor. Me apasiona el universo de la programación web, el diseño y desarrollo web y la tecnología en general. Aunque soy full-stack, mi pasión es el front-end, la terminal y crear cosas divertidas y locas.
Puedes encontrar más sobre mi en Manz.dev