16. Components

It's common, when developing an app or website, to run into repetitions of HTML blocks whose functionality is identical. To simplify development, and centralize the logic, there's a tendency to create components. An architecture technique where you split functionality into reusable fragments made up of the triad of HTML, CSS, and JavaScript.

With the arrival of modules, which we learned about in the previous lesson, we can create components natively (vanilla) without resorting to external frameworks like ReactJS, VueJS, or Angular. This is called Custom Elements.

Custom Elements

Starting from the following tag that I invented, with an attribute to indicate the figure, I can format a price with two decimals and the euro symbol.

<price-component amount="34.2"></price-component>

34.20 €

How is this possible? In JavaScript I just need to create a class that inherits from HTMLElement and register it with customElements.

// Component class
class PriceComponent extends HTMLElement {

    constructor() {
        super();
        this.textContent = `${parseFloat(this.getAttribute("cantidad")).toFixed(2)} €`;
    }

}

// When registering we define the component name, which must always be made up of at least two words.
customElements.define("price-component", PriceComponent);

You can create as many as your site needs, for all kinds of functionality: filtering, formatting, calculations, async connections... Although we're limited when it comes to including HTML. For that we'll include an HTML template.

Custom elements with an HTML template

In the following example you can see how a component has been declared with the goal of representing some cards for historical figures. They're fully configurable through their attributes and make use of a template to define the HTML structure.

<card-component
    name="Lope de Vega"
    country="Spain"
    avatar="https://upload.wikimedia.org/wikipedia/commons/thumb/1/1d/Lope_de_vega.JPG/640px-Lope_de_vega.JPG"
></card-component>

<card-component
    name="Francisco de Quevedo "
    country="Spain"
    avatar="https://upload.wikimedia.org/wikipedia/commons/thumb/8/84/Retrato_de_Francisco_de_Quevedo%2C_after_an_original_attributed_to_John_Vanderham.jpg/640px-Retrato_de_Francisco_de_Quevedo%2C_after_an_original_attributed_to_John_Vanderham.jpg"
></card-component>

<template id="card">
    <div>
        <article class="card">
            <header>
                <h2 class="card__name"></h2>
                <h3 class="card__country"></h3>
            </header>
            <p>
                <img class="card__avatar" src="" alt="Avatar">
            </p>
        </article>
    </div>
</template>

Writers as writers

The only difference is that at render time we'll capture the template's content and modify the tags we need.

class CardComponent extends HTMLElement {

    constructor() {
        super();
        this.render();
    }

    render() {
         // We get the template content
         const template = document.querySelector("template#card").content.firstElementChild;
         const miTemplate = template.cloneNode(true);
         // We modify the template's HTML
         miTemplate.querySelector(".card__name").textContent = this.getAttribute("name");
         miTemplate.querySelector(".card__country").textContent = this.getAttribute("country");
         miTemplate
             .querySelector(".card__avatar")
             .setAttribute("src", this.getAttribute("avatar"));
         // We insert the modified HTML
         this.appendChild(miTemplate);
    }
}

customElements.define("card-component", CardComponent);

Based on what we've defined, we can now build custom components as complex as we can imagine.

If you don't want to depend on a <template>, and want your component to be usable on every page, you must put the HTML inside your JavaScript.

Below you can see an adaptation of the previous code.

<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8"/>
         <title>Reusable example</title>
    </head>
    <body>
        <card-component
            name="Lope de Vega"
            country="Spain"
            avatar="https://upload.wikimedia.org/wikipedia/commons/thumb/1/1d/Lope_de_vega.JPG/640px-Lope_de_vega.JPG"
        ></card-component>

        <card-component
            name="Francisco de Quevedo"
            country="Spain"
            avatar="https://upload.wikimedia.org/wikipedia/commons/thumb/8/84/Retrato_de_Francisco_de_Quevedo%2C_after_an_original_attributed_to_John_Vanderham.jpg/640px-Retrato_de_Francisco_de_Quevedo%2C_after_an_original_attributed_to_John_Vanderham.jpg"
        ></card-component>


    <script>
/**
 * Example usage:
 *
 * <card-component
 *     name="Francisco de Quevedo"
 *     country="Spain"
 *     avatar="https://upload.wikimedia.org...original_attributed_to_John_Vanderham.jpg"
 *     ></card-component>
 */

class CardComponent extends HTMLElement {

    constructor() {
        super();
        // Get params
        const name = this.getAttribute("name");
        const country = this.getAttribute("country");
        const avatar = this.getAttribute("avatar");
        // Set HTML
        this.innerHTML = `
            <article class="card">
                <header>
                    <h2 class="card__name">${name}</h2>
                    <p class="card__country">${country}</p>
                </header>
                <p>
                    <img class="card__avatar" src="${avatar}" alt="Avatar">
                </p>
            </article>
        `;
    }
}

customElements.define("card-component", CardComponent);
    </script>
    </body>
</html>

Much cleaner, right? The only downside is that the web designer has to modify the HTML from the JavaScript, since they can't access the <template> tag. That said, if the component is very complex, it's better for the developer to handle it under their own supervision.

Activity 1

Create a custom component that limits a text, no matter how long, to 5 words. It must end with an ellipsis (...).

Activity 2

Create a component that shows a random image. If you want to make it more customizable, allow an attribute to be included with the image's theme.

Activity 3

Create a component to display a GitHub user's avatar. It should allow including the username and the avatar size.

Use the following URL as a hint: https://github.com/{username}.png.

This work is under a Attribution-NonCommercial-NoDerivatives 4.0 International license.

Desafíos de programación atemporales y multiparadigmáticos

Desafíos de programación atemporales y multiparadigmáticos

Te encuentras ante un librillo de actividades, divididas en 2 niveles de dificultad. Te enfrentarás a los casos más comunes que te puedes encontrar en pruebas técnicas o aprender conceptos elementales de programación.

Buy the book

Will you buy me a coffee?

This is how I keep writing without ads or paywalls.

Comments

There are no comments yet.