En este artículo vamos a utilizar el API de Elementos HTML personalizados) para crear nuevas etiquetas y utilizarlas en nuestro código HTML, en futuras publicaciones explicaremos como se usan en conjunto con los otros 3 estándares. Para crear un componente web se utilizan 4 tecnologías principales estándares de la web.

  • Custom elements o elementos HTML personalizados.
  • Shadow DOM, DOM en las sombras.
  • Carga de módulos o componentes externos.
    • HTML imports con la etiqueta <link rel="import">.
    • <script type="module">, cargando módulos usando el tag script. Esta tecnología sustituye a los html imports.

Para más información sobre componentes web te recomiendo que leas este artículo Cómo crear mejores aplicaciones con los fascinantes componentes web

Cada uno de estos ciclos es un evento único; dieta, elección, selección, estación, clima, digestión, descomposición y regeneración, difieren cada vez que ocurren. Por lo tanto, es el número de estos ciclos, grandes y pequeños, lo que decide el potencial de diversidad. Deberíamos sentirnos privilegiados de ser parte de esa renovación eterna. Con solo vivir hemos logrado la inmortalidad como hierba, saltamontes, gaviotas, gansos y otras personas. Somos parte de la diversidad que experimentamos en todos los sentidos.
Si, como nos aseguran los científicos, todos tenemos moléculas de Einstein, y si las partículas atómicas de nuestro cuerpo físico llegan a los límites más externos del universo, entonces todos somos en efecto componentes de todas las cosas. No nos queda ningún lugar adonde ir si ya estamos en todas partes, y esto es, en verdad, todo lo que tendremos o necesitaremos. Si nos amamos a nosotros mismos, deberíamos respetar todas las cosas por igual y no reclamar ninguna superioridad sobre lo que son, en efecto, nuestras otras partes. ¿Es la mano superior al ojo? ¿El hijo de la madre?

Bill Mollison

Elementos HTML Personalizados

El estándar Custom elements nos indica dos maneras de extender la funcionalidad del HTML:

  • Crear nuestros propios elementos desde cero, nuevos elementos a los que le agregamos toda la funcionalidad que sea necesaria.
  • Extender la funcionalidad de elementos ya existentes. Lamentablemente esta característica no está soportada por todos los navegadores modernos. En la versión v0 si había soporte, pero esa versión ya está obsoleta.
    • Desde la versión 67 de chrome esto funciona correctamente, Firefox lo tiene en desarrollo, Edge lo va a implementar, lamentablemente safari decidió no implementarlo.
    • Más información en https://www.chromestatus.com/feature/4670146924773376

Para entender la diferencia, en el primer caso podríamos crear un elemento llamado <mi-boton>, al que tendríamos que agregar estilos y funcionalidad desde cero para que se comporte como un botón.

En el segundo, podemos crear un elemento <mi-boton-extendido>, el cual extiende de la etiqueta <button>, entonces este componente si tendría los estilos, eventos y demás comportamientos de un botón, es decir, hereda el comportamiento de <button> y entonces solo se le agregaría algún comportamiento extra para cumplir con el objetivo en mente.

Si en los ejemplos no lanza el alerta especificado en el código, siempre puedes presionar “edit on codepen” para que el alerta funcione y puedes ver el código en un espacio más grande.

Crear un nuevo elemento desde cero

Veamos la primera forma para crear un nuevo tipo de elemento, desde cero.

Hay algunas cosas importantes que remarcar en el código:

  • Todos los elementos deben tener un prefijo, esto es un requisito para evitar que en futuras versiones de HTML las nuevas etiquetas colisionen con los elementos creados por ti u otros programadores.
  • Podemos registrar un elemento en dos pasos, creando la clase y pasando su referencia como parámetro en el método customElements.define o utilizar una clase anónima como se hizo con <mi-boton-2> directamente en el método customElements.define.
  • Una clase en Javascript no es una clase como en otros lenguajes de programación orientada a objetos, realmente es como un tipo de función especial que te permite utilizar herencia de prototipos de una forma más familiar y limpia.
  • En el constructor siempre debemos de invocar la función super(), de esta manera obtenemos la herencia, pues provoca que se invoque este mismo método en la cadena de prototipos/herencia. Si no se invoca, el navegador web te mostrara un caught ReferenceError: Must call super constructor in derived class before accesing 'this' or returning from derived constructor.
  • Para establecer estilos se usa la nuevas etiqueta creadas mi-boton and mi-boton-2. Al ser nuevas etiquetas, cualquier selector de css puede funcionar, hablamos de selector de clase, el id, pseudoselectores y pseudoelementos.

Extender la funcionalidad de elementos ya existentes

Ahora vamos a crear un nuevo elemento basado en uno ya existente, solo pondremos el ejemplo del código, realmente como mencionamos antes, no todos navegadores aún soportan este estándar, esperemos que en futuras versiones lo hagan.

Las principales diferencias cuando extendemos la funcionalidad de elementos nativos es que seguimos utilizando el nombre de la etiqueta nativa y agregamos el atributo is para indicar que será del tipo elemento que definimos. Esto en código HTML.

Pero en código Javascript es necesario en la definición (método customElements.define), utilizar un tercer parámetro donde podemos indicar de que elemento HTML vamos a extender, extends. Y definir la propiedad is a través de código JS.

Métodos del ciclo de vida de un elemento

The custom elements tiene funciones que se ejecutan en un determinado momento de su existencia, de tal manera que el autor puede ejecutar acciones, cambios o inicialización de datos en uno o mas momentos del ciclo de vida de un componente.

Si revisas la consola de la página creada por codepen, el orden en que son ejecutados estos callbacks es:

constructor: Cuando el elemento es creado
attributeChangedCallback: Cuando cambia un atributo
connectedCallback: Cuando el elemento es insertado en el documento

Para probar disconnectedCallback solo inspecciona el elemento <mi-mensaje> y elimínalo. Un mensaje de alerta parecerá informando de la eliminación.

Cuando se crea el elemento, attributeChangedCallback se ejecuta al definir por primera vez el atributo antes de que el elemento sea insertado en el DOM.

Sincronización de atributos y propiedades

Recuerda que las propiedades y los atributos son cosas diferentes, aunque los atributos se ocupen para reflejar el estado de un elemento, no son lo mismo. Un atributo se ocupa normalmente para declarar información del elemento en el código HTML y reflejar su estado, una propiedad forma parte de la instancia del elemento.

Para obtener un atributo se utiliza element.getAttribute(attributeName), para obtener una propiedad se utiliza element.property.

To define an attribute, use element.setAttribute(attributeName, value), to define a property we use element.property = value.

Attributes are stored in a property element.attributes de la instancia del elemento.

Para entender mejor esto, vamos a crear un ejemplo un poco más complejo, donde se implementa la sincronización de atributos, propiedades y el estado del elemento. Este nuevo elemento consta de dos propiedades principales, msj and casiVisible, estos se sincronizan con los atributos msj and casi-visible.

Cuando el atributo casi-visible está declarado, el elemento se opaca hasta casi  desaparecer, además el borde negro desaparece.

Cuando decimos que se sincronizan, es que un cambio en la propiedad, se refleja en el atributo y viceversa.

Puedes hacer varias pruebas, inspeccionando los elementos, agrega y elimina el atributo casi-visible.

También puedes inspeccionar el elemento y modificar el atributo msj para ver que sucede.

Las mismas pruebas las puedes hacer con las propiedades, utilizando google chrome inspecciona los elementos y en la consola ejecutas el código de abajo:

$0.msj = 'HOLA MODIFICANDO EL MENSAJE';
$0.casiVisible = false; // muestra el elemento
$0.casiVisible = true; // casi oculta el elemento

En todos los casos notarás cambios en el estado del elemento y en el contenido del mensaje.

En siguientes publicaciones veremos como utilizar el estándar template o plantilla y shadow DOM (DOM en las sombras), luego veremos el uso JS modules, para al final crear componentes completos y ver como se comunican entre ellos.

en_USEN