Cuando trabajamos con Web Components, una de las opciones más interesantes que tenemos es la posibilidad de crear componentes personalizados que extienden elementos HTML nativos. Esto nos permite añadir funcionalidades específicas sin perder las ventajas inherentes a esos elementos, como la accesibilidad o el comportamiento esperado por el navegador y los usuarios.
Imaginemos que queremos mejorar un elemento select tradicional. En lugar de construir un componente desde cero, que podría ser complejo y requerir mucha lógica para integrarse bien con formularios y accesibilidad, podemos crear un componente que extienda el HTMLSelectElement. De esta forma, mantenemos toda la funcionalidad nativa, pero añadimos botones para navegar entre las opciones, algo que no existe por defecto en HTML. Este enfoque se conoce como customized built-in elements.
Una ventaja clara de extender elementos nativos es que no tenemos que preocuparnos por reimplementar características como el control por teclado o la compatibilidad con lectores de pantalla. Si fabricamos un componente autónomo desde cero, tendríamos que invertir tiempo en asegurarnos de que estas funcionalidades estén bien cubiertas, lo que puede ser costoso y propenso a errores. En cambio, al extender un elemento nativo, heredamos automáticamente estas capacidades.
Sin embargo, hay que tener en cuenta que esta técnica no está soportada en todos los navegadores. Por ejemplo, Safari y otros basados en WebKit no implementan customized built-in elements, lo que limita su uso en ciertos entornos. Para estos casos, existen polyfills que pueden ayudar a suplir esta carencia, aunque no siempre son perfectos.
Para ilustrar cómo crear un customized built-in element, podemos construir un botón que se desactive automáticamente cuando se pulsa y cambie su texto para indicar que está procesando una acción, como guardando. En lugar de añadir JavaScript externo que busque botones con ciertas clases o atributos y les aplique la lógica, podemos encapsular todo dentro de un componente que extienda HTMLButtonElement.
El código básico para este botón personalizado sería algo así:
class DesactivableButton extends HTMLButtonElement {
constructor() {
super();
this._onButtonClick = this._onButtonClick.bind(this);
}
connectedCallback() {
this.addEventListener('click', this._onButtonClick);
if (!this.visitext) {
this.visitext = 'guardando';
}
}
disconnectedCallback() {
this.removeEventListener('click', this._onButtonClick);
}
static get observedAttributes() {
return ['visitext'];
}
attributeChangedCallback(name, oldVal, newVal) {
if (name === 'visitext') {
this.visitext = newVal;
}
}
_onButtonClick() {
this.disabled = true;
this.innerText = this.visitext;
}
}
customElements.define('desactivable-button', DesactivableButton, { extends: 'button' });
Para usar este componente, simplemente añadimos un botón en HTML con el atributo is apuntando al nombre del componente personalizado:
<button is="desactivable-button" visitext="enviando datos">Guardar cambios</button>
Cuando pulsamos este botón, se desactiva automáticamente y cambia su texto a enviando datos. Si no especificamos el atributo visitext, usará el valor por defecto guardando.
Este enfoque nos permite aprovechar toda la funcionalidad nativa del botón, incluyendo la accesibilidad y el comportamiento esperado en formularios, mientras añadimos la lógica personalizada que necesitamos. Además, al estar encapsulado en un componente, podemos reutilizarlo fácilmente en diferentes partes de nuestra aplicación sin repetir código ni añadir scripts externos que busquen elementos específicos.
En definitiva, los customized built-in elements son una herramienta poderosa para crear componentes web que combinan lo mejor de los elementos nativos con la flexibilidad de los Web Components, siempre teniendo en cuenta las limitaciones actuales de soporte en navegadores y la necesidad de usar polyfills cuando sea necesario.