Un tutorial introductorio a los elementos personalizados
Escribí mi primer elemento personalizado usando una biblioteca CSS llamada CSS Doodle. Es una aplicación increíble que usa elementos personalizados para permitirte crear animaciones CSS impresionantes. Eso despertó en mí un deseo completamente nuevo de descubrir cómo funciona esa cosa bajo el capó. Así que decidí analizar más de cerca los componentes web en general, un tema sobre el que muchas personas me pidieron que escribiera.
Los elementos personalizados nos permiten crear nuevas etiquetas HTML.
No podía imaginarme por qué esto podría ser útil hasta que usé la biblioteca CSS Doodle. Después de todo, ya tenemos muchas etiquetas.
Este tutorial cubre la versión 1 de Custom Elements, la última versión del estándar al momento de escribir este artículo.
Usando elementos personalizados podemos crear una etiqueta HTML personalizada con CSS y JavaScript asociados.
No es una alternativa a frameworks como React, Angular o Vue, pero es un concepto completamente nuevo.
El window
objeto global expone una customElements
propiedad que nos da acceso a un CustomElementRegistry
objeto.
El CustomElementRegistryobjeto
Este objeto tiene varios métodos que podemos usar para registrar elementos personalizados y consultar elementos personalizados ya registrados:
define()
se utiliza para definir un nuevo elemento personalizadoget()
se utiliza para obtener el constructor de un elemento personalizado (devuelveundefined
si no existe)upgrade()
para actualizar un elemento personalizadowhenDefined()
Se utiliza para obtener el constructor de un elemento personalizado. Es similar aget()
, pero devuelve una promesa que se resuelve cuando el elemento está disponible.
Cómo crear un elemento personalizado
Antes de poder llamar al window.customElements.define()
método, debemos definir un nuevo elemento HTML creando una nueva clase que extienda la clase incorporada HTMLElement:
class CustomTitle extends HTMLElement { //...}
Dentro del constructor de clase, vamos a utilizar Shadow DOM para asociar CSS, JavaScript y HTML personalizados a nuestra nueva etiqueta.
De esta manera, todo lo que veremos en el HTML es nuestra etiqueta, pero esto encapsulará mucha funcionalidad.
Comenzamos inicializando el constructor:
class CustomTitle extends HTMLElement { constructor() { super() //... }}
Luego, llamamos al attachShadow()
método de HTMLElement pasando un objeto con la mode
propiedad establecida en 'open'
. Esta propiedad establece el modo de encapsulación para el shadow DOM. Si es , open
podemos acceder a la shadowRoot
propiedad de un elemento. Si está cerrado, no podemos.
A continuación te explicamos cómo hacerlo:
class CustomTitle extends HTMLElement { constructor() { super() this.attachShadow({ mode: 'open' }) //... }}
Encontrarás algunos ejemplos que usan la sintaxis const shadowRoot = this.attachShadow(/* ... */)
pero puedes evitarlo a menos que lo configures mode
, closed
ya que siempre puedes hacer referencia a ese objeto llamando a this.shadowRoot
.
Esto es lo que vamos a hacer ahora para configurar el innerHTML:
class CustomTitle extends HTMLElement { constructor() { super() this.attachShadow({ mode: 'open' }) this.shadowRoot.innerHTML = ` h1My Custom Title!/h1 ` }}
Puedes agregar tantas etiquetas como quieras, no estás limitado a una etiqueta dentro de la
innerHTML
propiedad
Ahora agregamos este elemento recién definido a window.customElements
:
window.customElements.define('custom-title', CustomTitle)
¡Y podemos usar el custom-title/custom-title
Elemento Personalizado en la página!
Nota: no se pueden utilizar etiquetas de cierre automático (en otras palabras: esto
custom-title /
no está permitido por el estándar)
Observe el -
guion en el nombre de la etiqueta. Debemos usar un guion en un elemento personalizado. Así es como podemos distinguir una etiqueta integrada de una personalizada.
Ahora tenemos este elemento en la página y podemos hacer lo que hacemos con otras etiquetas: ¡orientarlo con CSS y JavaScript!
Proporcionar un CSS personalizado para el elemento
En el constructor, puedes pasar una style
etiqueta además de la etiqueta HTML que define el contenido, y dentro de ella puedes tener el CSS del elemento personalizado:
class CustomTitle extends HTMLElement { constructor() { super() this.attachShadow({ mode: 'open' }) this.shadowRoot.innerHTML = ` style h1 { font-size: 7rem; color: #000; font-family: Helvetica; text-align: center; } /style h1My Custom Title!/h1 ` }}
Aquí está el ejemplo del elemento personalizado que creamos en Codepen: https://codepen.io/flaviocopes/pen/LKgjzK/ Armario escobero
Una sintaxis más corta
En lugar de definir primero la clase y luego llamarla, window.customElements.define()
también podemos usar esta sintaxis abreviada para definir la clase en línea :
window.customElements.define('custom-title', class extends HTMLElement { constructor() { ... }})
Añadir JavaScript
Al igual que hicimos con CSS, podemos incorporar JavaScript.
Sin embargo, no podemos agregarlo directamente a la etiqueta de plantilla como lo hicimos para CSS.
Aquí defino un detector de eventos de clic en el constructor del elemento personalizado:
class CustomTitle extends HTMLElement { constructor() { super() this.attachShadow({ mode: 'open' }) this.shadowRoot.innerHTML = ` h1My Custom Title!/h1 ` this.addEventListener('click', (e) = { alert('clicked!') }) }}
Alternativa: utilizar plantillas
En lugar de definir el HTML y el CSS en una cadena de JavaScript, puedes usar una template
etiqueta en HTML y asignarle un id
:
template style h1 { font-size: 7rem; color: #000; font-family: Helvetica; text-align: center; } /style h1My Custom Title!/h1/templatecustom-title/custom-title
Luego, puedes hacer referencia a él en tu constructor de elementos personalizados y agregarlo al Shadow DOM:
class CustomTitle extends HTMLElement { constructor() { super() this.attachShadow({ mode: 'open' }) const tmpl = document.querySelector('#custom-title-template') this.shadowRoot.appendChild(tmpl.content.cloneNode(true)) }}window.customElements.define('custom-title', CustomTitle)
Ejemplo en Codepen: https://codepen.io/flaviocopes/pen/oramEY/
Ganchos de ciclo de vida
Además de constructor
, una clase de elemento personalizado puede definir aquellos métodos especiales que se ejecutan en momentos especiales durante el ciclo de vida del elemento:
connectedCallback
cuando el elemento se inserta en el DOMdisconnectedCallback
cuando el elemento se elimina del DOMattributeChangedCallback
Cuando un atributo observado cambia, se agrega o se eliminaadoptedCallback
Cuando el elemento se ha movido a un nuevo documento
class CustomTitle extends HTMLElement { constructor() { ... } connectedCallback() { ... } disconnectedCallback() { ... } attributeChangedCallback(attrName, oldVal, newVal) { ... }}
attributeChangedCallback()
obtiene 3 parámetros:
- el nombre del atributo
- el antiguo valor del atributo
- el nuevo valor del atributo.
Mencioné que escucha los atributos observados. ¿Cuáles son? Debemos definirlos en una matriz devuelta por el observedAttributes
método estático:
class CustomTitle extends HTMLElement { constructor() { ... } static get observedAttributes() { return ['disabled'] } attributeChangedCallback(attrName, oldVal, newVal) { ... }}
He definido el disabled
atributo que se debe observar. Ahora, cuando cambia, por ejemplo en JavaScript, lo configuramos disabled
como verdadero:
document.querySelector('custom-title').disabled = true
los attributeChangedCallback()
incendios con el conjunto de parámetros 'disabled', false, true
.
Nota:
attributeChangedCallback()
se puede llamar al elemento mediante JavaScript (por algún motivo que desconozco), pero no debería hacerlo. El navegador debería invocarlo automáticamente.
Definir atributos personalizados
Puede definir atributos personalizados para sus elementos personalizados agregando un captador y un definidor para ellos:
class CustomTitle extends HTMLElement { static get observedAttributes() { return ['mycoolattribute'] } get mycoolattribute() { return this.getAttribute('mycoolattribute') } set mycoolattribute(value) { this.setAttribute('mycoolattribute', value) }}
Así es como puedes definir atributos booleanos, que son “verdaderos” si están presentes, como disabled
para los elementos HTML:
class CustomTitle extends HTMLElement { static get observedAttributes() { return ['booleanattribute'] } get booleanattribute() { return this.hasAttribute('booleanattribute') } set booleanattribute(value) { if (value) { this.setAttribute('booleanattribute', '') } else { this.removeAttribute('booleanattribute') } }}
Cómo diseñar un elemento personalizado que aún no está definido
Es posible que JavaScript tarde un poco en activarse y que un elemento personalizado no se defina tan pronto como se cargue la página. La página puede presentar un diseño desagradable cuando se agrega el elemento a la página.
Para resolver este problema, agregue una :not(:defined)
pseudoclase CSS que establezca la altura y se desvanezca en el elemento cuando esté disponible:
custom-title:not(:defined) { display: block; height: 400px; opacity: 0; transition: opacity 0.5s ease-in-out;}
¿Puedo usarlos en todos los navegadores?
Las versiones actuales de Firefox, Safari y Chrome son compatibles con ellos, pero IE nunca lo será y, al momento de escribir este artículo, Edge ya tiene soporte para ellos en desarrollo.
Puedes usar este polyfill para agregar un mejor soporte también para navegadores más antiguos.
Tal vez te puede interesar:
- Introducción a React
- Agregar evento de clic a los elementos DOM devueltos desde querySelectorAll
- Cómo cambiar el valor de un nodo DOM
- Cómo comprobar si un elemento DOM tiene una clase
Elementos personalizados de componentes web
El CustomElementRegistryobjetoCómo crear un elemento personalizadoProporcionar un CSS personalizado para el elementoUna sintaxis más cortaAñadir JavaScriptA
programar
es
https://aprendeprogramando.es/static/images/programar-elementos-personalizados-de-componentes-web-2091-0.jpg
2024-11-01
Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente