Logo de islavisual
Isotipo de islavisual IslaVisual
imagen de sección

Ultima revisión 31/12/2022

Cómo crear Web Components de forma usable y accesible

Para terminar el año, os mostraré cómo crear un web component desde un punto de vista más reutilizable, usable y accesible de modo sencillo y rápido. De hecho, es más rápido y pesa infinitamente menos que su equivalente en frameworks como Angular o React.

NOTA: Si no sabéis mucho sobre Web Components, siempre podéis echarle un vistazo a la web de MDN, en la url Web Components de Developer Mozilla.

class App extends HTMLElement{
    constructor() {
        super();
        this.shadow = null;
        this.name = this.constructor.name.toLocaleLowerCase();
        console.log(this.name)
    }

    connectedCallback() {
        this.shadow = this.attachShadow({ mode: "open" });
        this.shadow.innerHTML = this.getStyles() + this.getView();
    }

    getStyles(){
        if(this.name == "app" || this.name == undefined){
            let link = document.createElement("link");
            link.href = App.path + "app.css";
            link.rel = "stylesheet";
            document.querySelector("head").append(link);
            return "";

        } else {
            return ``;
        }        
    }

    getView(){
        this.getHTML(App.path + (this.name == "app" ? '': (this.name  + '/')) + this.name+'.html');
        return '';
    }

    async getHTML (path) {
        let request = await fetch(path);
        if (!request.ok) return;

        this.shadow.innerHTML += await request.text();
    }
}

App.path = document.currentScript.src.replace(document.currentScript.src
           .split("/").reverse()[0], "").replace("/js", "")
App.prototype.getStyles();

Archivo app.js

En el código anterior, podemos ver cómo se ha definido una clase denominada App que tiene un método constructor, el método connectedCallback y otros tres más. El primero de ellos, el llamado getStyles(), es el que nos cargará los estilos asociados al componente que se esté invocando. El segundo, el llamado getView(), es el que nos cargará el código HTML asociado a dicho componente web y, el tercero, llamado getHTML(), es el que nos permitirá leer, en este caso, el HTML de un archivo externo.

Para que este componente funcione correctamente, deberemos crear una carpeta llamada components y, ahí, crear los archivos app.js (donde estará este código), app.css (dónde podrán estar los estilos globales para todo componente que creemos posteriormente) y app.html con el cógido que deseemos usar previamente antes de cargar cualquier componente dependiente de este componente base. No obstante, en este ejemplo no lo usaremos, por lo que lo dejaremos vacío.

Después de definir la clase, definiremos la URL base del componente App. el cuál nos servirá para crear nuevos componenetes web de una forma más rápida y sencilla. Y, por último, lo que hace el código anterior es llamar a getStyles(), que inserta el código en el HEAD de la página, aunque esto es un paso omitible.

Una vez que tengamos el direciorio creado con estos archivos, lo que haremos es crear otro subdirectorio o carpeta dentro, que en este caso llamaremos todoapp.

Dentro de la carpeta todoapp, crearemos los archivos todoapp.js, donde estará elJavaScript de nuestro Web Component dependiente del base llamada (App), todoapp.css, dónde se incorporarán los estilos propios para este componente y, todoapp.html, dónde estará el cógido HTML del actual componente.

:host, :root {
    --color: 255, 255, 255;
    --bg: 40, 44, 52;
    --button: 252, 146, 158;
    --input: 121, 182, 242;
    --inverted: 0, 0, 0;
    --space: 1.5em;
}

* {
    box-sizing: border-box;
    color: rgba(var(--color), 1);
    background: rgba(var(--bg), 1);
    font-family: Segoe UI, sans-serif;
    font-weight: 400;
    font-style: normal;
}

section {
    background: rgba(var(--bg), 1);
    padding: 10px;
}

h1 {
    font-size: 18px;
    font-weight: bold;
    margin: 10px 0
}

label {
    display: block;
    margin-top: 10px;
}

input {
    width: 100%;
    display: block;
    border: 1px solid rgba(var(--bg), 0.5);
    background: rgba(var(--input), 1);
    color: rgba(var(--inverted), 1);
    padding: 5px;
    margin: 5px 0 0 0;
    overflow: visible;
    font-family: inherit;
    font-size: 100%;
    line-height: 1.15;
}

[type=button], [type=reset], [type=submit], button {
    -webkit-appearance: button;
    margin-top: 10px;
    padding: 5px 10px;
    background: rgba(var(--button), 1);
    color: rgba(var(--inverted), 1);
    border: 1px solid rgba(var(--color), 0.5);
}

ul {
    margin-top: 20px;
    font-size: 16px;
    color: #1a1a1a;
    padding-left: 20px;
}

Archivo app.css

class TodoApp extends App {
    constructor() {
        super();
        window.todoApp = TodoApp.prototype
    }

    handleSubmit(e) {
        e.preventDefault();
        
        let ul  = e.target.getRootNode().querySelector("ul");
        let inp = e.target.getRootNode().querySelector("input");
        let li = document.createElement("li")
            li.innerHTML = inp.value;

        ul.append(li)
        return false;
        
    }
}

window.customElements.define("todo-app", TodoApp);

Archivo todoapp.js

/*Lo que se desee poner como estilos específicos*/

Archivo todoapp.css

<section>
    <h1>Tareas pendientes</h1>
    <ul></ul>
    <form name="frmTodoApp" method="get" onsubmit="return todoApp.handleSubmit(event);">
        <label htmlFor="new-todo">
            ¿Qué se necesita hacer?
        </label>
        <input
            id="new-todo"
            onChange={this.handleChange}
            placeholder="Introduzca una tarea"
        />
        <button>Añadir</button>
    </form>
</section>

Archivo todoapp.html

Si deseáis más información Web Components y JavaScript podéis adquirir mi libro Domine JavaScript 4ª Edición, de la editorial RA-MA y disponible también en Google Books.

Sobre el autor

Imagen de Pablo Enrique Fernández Casado
Pablo Enrique Fernández Casado

CEO de IslaVisual, Manager, Full Stack Analyst Developer y formador por cuenta ajena con más de 25 años de experiencia en el campo de la programación y más de 10 en el campo del diseño, UX, usabilidad web y accesibilidad web. También es escritor y compositor de música, además de presentar múltiples soft kills como la escucha activa, el trabajo en equipo, la creatividad, la resiliencia o la capacidad de aprendizaje, entre otras.

Especializado en proveer soluciones integrales de bajo coste y actividades de consultoría de Usabilidad, Accesibilidad y Experiencia de Usuario (UX), además de ofrecer asesoramiento en SEO, optimización de sistemas y páginas web, entre otras habilidades.