Nei moderni browser è disponibile una nuova API CustomElementRegistry che permette di creare degli elementi HTML custom.

In pratica, similmente a come avviene in React creando un componente, è possibile fare una cosa simile nativamente nel browser (ovviamente escluso Internet Explorer che ormai è superato).
Ricarica la pagina per far ripartire il countdown.

Implementazione

Ad esempio è possibile creare un nuovo elemento HTML sgart-countdown che visualizza il decremento di un numero preimpostato (attributo start-count) ogni secondo

HTML

<sgart-countdown start-count="12"></sgart-countdown>

<script type="text/javascript" src="sgart-web-components.js"></script>
Attenzione il nome del nuovo elemento HTML deve contenere il segno meno - questo per evitare conflitti con i nomi dei tag standard presenti e futuri.
L'implementazione del codice associato lo si fa con i seguenti passi:
  • si crea una nuova classe (class) che eredita da HTMLElement
  • si associa il nome del nuovo elemento alla classe, con customElements.define(nome, classe)

Nel costruttore della classe:
  • va richiamato, come prima cosa, il metodo super() (obbligatorio)
  • si crea un elemento shadow con this.attachShadow({ mode: 'open' });
  • si aggiungono gli elementi HTML necessari (wrapper) e gli eventuali stili CSS (style)
  • sia aggancia (attach) gli elementi e gli stili all'elemento shadow (this.shadowRoot.append(style, wrapper)
  • se necessario si implementano gli eventi (lifecycle callbacks)

Il tutto si traduce con questo codice JavaScript

JavaScript: sgart-web-components.js

// Autonomous custom elements
class SgartCountdown extends HTMLElement {
    constructor() {
        // Always call super first in constructor
        super();

        // Create a shadow root (open = can access via JavaScript in main page with Element.shadowRoot)
        this.attachShadow({ mode: 'open' }); // sets and returns 'this.shadowRoot'

        // Element functionality written in here
        const wrapper = document.createElement('span');

        wrapper.setAttribute('class', 'wrapper');

        // Create some CSS to apply to the shadow dom
        const style = document.createElement('style');
        style.textContent = `
            .wrapper { 
                display: inline-block;
                width: 100px;
                text-align: center;
                border: 1px solid red;
            }
            .end {
                background-color: #800;
            }`;

        // attach the created elements to the shadow DOM
        this.shadowRoot.append(style, wrapper);

        this._wrapper = wrapper;
        this.readProperty();
        this.start();
    }

    readProperty() {
        this._count = parseInt(this.getAttribute('start-count'));
        if (isNaN(this._count) || this._count < 1) {
            this._count = 0;
        }
    }

    start() {
        this.update();
        this._timer = setInterval(() => {
            if (this._count > 0)
                this._count--;
            this.update(this._count);
        }, 1000);
    }

    update() {
        this._wrapper.innerHTML = this._count;
        if (this._count <= 0) {
            if (this._timer !== null)
                clearInterval(this._timer);
            this._wrapper.classList.add('end');

        }
    }

    /*
     * lifecycle callbacks
     */

    // invoked each time the custom element is appended into a document-connected element. This will happen each time the node is moved, and may happen before the element's contents have been fully parsed.
    connectedCallback() {
        console.log('SgartCountdown added to page.');
        this.shadowRoot.addEventListener('click', e => {
            console.log('event', e);
        });
    }

    // invoked each time the custom element is disconnected from the document's DOM.
    disconnectedCallback() {
        console.log('SgartCountdown removed from page.');
    }

    // Invoked each time the custom element is moved to a new document.
    adoptedCallback() {
        console.log('SgartCountdown moved to new page.');
    }


    // per gestire una proprietà con attributeChangedCallback devo primo osservarla (observe) con un motodo statico
    static get observedAttributes() { return ['start-count']; }

    // Invoked each time one of the custom element's attributes is added, removed, or changed. Which attributes to notice change for is specified in a static get observedAttributes method
    attributeChangedCallback(name, oldValue, newValue) {
        console.log('SgartCountdown attributes changed.', name, oldValue, newValue);
    }
}

// defines a new custom element
customElements.define('sgart-countdown', SgartCountdown);

A questo punto abbiamo un nuovo elemento HTML che possiamo riusare anche più volte nella stessa pagina con attributi diversi (vedi esempio a inizio pagina)

HTML

<sgart-countdown start-count="12"></sgart-countdown>
<sgart-countdown start-count="24"></sgart-countdown>
<sgart-countdown start-count="7"></sgart-countdown>
<sgart-countdown start-count="0"></sgart-countdown>

<script type="text/javascript" src="sgart-web-components.js"></script>
Tags:
CSS19 HTML74 HTML 554 JavaScript184 Vanilla JS24
Potrebbe interessarti anche: