W3docs

Tecniche Avanzate del DOM

Padroneggiare le tecniche avanzate del DOM migliora lo sviluppo web, permettendo di creare codice dinamico, modulare e manutenibile senza framework.

Padroneggiare le tecniche avanzate del DOM ti aiuta a costruire interfacce dinamiche, modulari e manutenibili con JavaScript puro — senza alcun framework. Questa guida tratta due pilastri del lavoro moderno con i componenti: l'elemento <template> per definire markup riutilizzabile e inerte, e lo Shadow DOM per incapsulare la struttura, gli stili e il comportamento di un componente. Insieme costituiscono la base su cui si fondano i web component e gli elementi personalizzati.

Creazione e utilizzo dei template

Perché esistono i template

Prima dell'elemento <template>, gli sviluppatori costruivano markup riutilizzabile inserendo HTML in elementi <div> nascosti, letterali di stringa JavaScript o blocchi <script type="text/template">. Ogni approccio ha uno svantaggio: i <div> nascosti costano comunque al browser il parsing e il caricamento delle risorse (le immagini vengono caricate, gli script eseguiti), mentre i template in formato stringa perdono la colorazione della sintassi e sono facili da rompere.

L'elemento <template> risolve questo problema. Il suo contenuto viene analizzato ma rimane inerte: il browser costruisce i nodi del DOM ma non li renderizza, non esegue i loro script e non carica le immagini o i media finché non si clona esplicitamente il contenuto nel documento attivo. Questo rende <template> lo strumento corretto per dichiarare markup che si intende istanziare molte volte.

Utilizzo dell'elemento <template>

L'elemento <template> ti permette di definire HTML che non viene renderizzato al caricamento della pagina. Puoi accedere al suo contenuto tramite la proprietà di sola lettura content, che restituisce un DocumentFragment.

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Using the <template> Element</title>
</head>
<body>
    <template id="my-template">
        <div class="card">
            <h2>Title</h2>
            <p>Content goes here...</p>
        </div>
    </template>
    <button id="show-template">Show Template</button>
    <div id="content"></div>

    <script>
        document.getElementById('show-template').addEventListener('click', () => {
            const template = document.getElementById('my-template');
            const content = document.getElementById('content');
            const clone = template.content.cloneNode(true);
            content.appendChild(clone);
        });
    </script>
</body>
</html>

Questo esempio mostra la struttura di base di un elemento <template> contenente una scheda con titolo e contenuto. Il contenuto del template viene clonato e inserito nel DOM quando si fa clic sul pulsante. Per un approfondimento sull'elemento in sé, vedi l'elemento <template>.

Clonazione e inserimento del contenuto del template

Per riutilizzare un <template>, clona il suo content e inserisci il clone nel DOM. Passa sempre true a cloneNode affinché l'intero sottoalbero (l'elemento e tutti i suoi discendenti) venga copiato — cloneNode(false) copia solo il nodo superiore e restituirebbe un frammento vuoto.

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Cloning and Inserting Template Content</title>
</head>
<body>
    <template id="card-template">
        <div class="card">
            <h2 class="title"></h2>
            <p class="body"></p>
        </div>
    </template>

    <button id="add-card">Add Card</button>
    <div id="container"></div>

    <script>
        let count = 0;
        document.getElementById('add-card').addEventListener('click', () => {
            const template = document.getElementById('card-template');
            const clone = template.content.cloneNode(true);

            // Fill the clone with dynamic data before inserting it.
            count++;
            clone.querySelector('.title').textContent = 'Card ' + count;
            clone.querySelector('.body').textContent = 'Created at ' + new Date().toLocaleTimeString();

            document.getElementById('container').appendChild(clone);
        });
    </script>
</body>
</html>

Il vero valore dei template sta in questo schema: clona, poi popola il clone con i dati prima di inserirlo. Alcuni dettagli da tenere a mente:

  • Un DocumentFragment si svuota quando viene aggiunto. Dopo appendChild(clone), i figli del frammento si spostano nel contenitore e il frammento rimane vuoto — quindi chiama cloneNode una volta per ogni elemento che vuoi aggiungere.
  • Esegui le query sul clone, non sul documento. Selettori come clone.querySelector('.title') operano sul frammento non ancora inserito, così lo popoli prima che raggiunga il DOM attivo (evitando reflow aggiuntivi). Vedi ricerca con querySelector.
  • document.importNode(template.content, true) è l'equivalente per documenti diversi — usalo quando il template si trova in un altro documento o iframe, affinché i nodi importati appartengano al documento corrente.

Shadow DOM

Introduzione allo Shadow DOM

Lo Shadow DOM è uno standard web che abilita l'incapsulamento nei web component. Collega un albero DOM separato e nascosto — il shadow tree — a un elemento (il shadow host). I nodi all'interno di quell'albero non sono raggiungibili dal normale document.querySelector della pagina, e gli stili definiti al suo interno non fuoriescono nel resto della pagina. Questo mantiene la struttura interna, gli stili e il comportamento di un componente isolati dal documento globale.

Alcuni termini che incontrerai frequentemente:

  • Shadow host — l'elemento normale a cui è collegato il shadow tree.
  • Shadow root — il nodo radice del shadow tree, restituito da attachShadow().
  • Shadow boundary — il confine tra il shadow tree e il resto del documento che il sistema di scope non attraversa.

Modalità open e closed

attachShadow() richiede un'opzione mode:

const open = host.attachShadow({ mode: 'open' });
// host.shadowRoot  →  the shadow root (accessible from outside)

const closed = host2.attachShadow({ mode: 'closed' });
// host2.shadowRoot →  null (the root is hidden from outside scripts)

In pratica, preferisci open. La modalità closed non fornisce vera sicurezza — chiunque può sovrascrivere attachShadow prima che il tuo codice venga eseguito — e rende solo il componente più difficile da testare e debuggare.

Incapsulamento e sviluppo basato su componenti

L'incapsulamento garantisce che gli stili e gli script definiti all'interno di un componente non fuoriescano e non influenzino il resto del documento — e che gli stili esterni non penetrino al suo interno. L'esempio seguente collega una shadow root, poi costruisce il suo contenuto in un DocumentFragment affinché l'intero sottoalbero venga inserito in una singola operazione. Un elemento <slot> proietta il contenuto esistente dell'host (il "light DOM") nel shadow tree insieme al markup proprio del componente.

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Shadow DOM Example</title>
    <style>
        .card {
            padding: 20px;
            margin: 10px;
            border: 1px solid #ccc;
        }
    </style>
</head>
<body>
    <div id="shadow-host" class="card">
        <span>This is the light DOM content</span>
    </div>

    <script>
        const host = document.getElementById('shadow-host');
        const shadowRoot = host.attachShadow({ mode: 'open' });

        const fragment = document.createDocumentFragment();
        const style = document.createElement('style');
        style.textContent = `.shadow-card { padding: 20px; margin: 10px; border: 1px solid blue; color: blue; }`;
        const slot = document.createElement('slot');
        const card = document.createElement('div');
        card.className = 'shadow-card';
        card.textContent = 'This is inside the Shadow DOM';

        fragment.appendChild(style);
        fragment.appendChild(slot);
        fragment.appendChild(card);
        shadowRoot.appendChild(fragment);
    </script>
</body>
</html>

Questo esempio crea un shadow tree su #shadow-host e vi inietta stili e contenuto. Il contenuto del light DOM (This is the light DOM content) rimane nell'host ed è esposto all'interno del shadow tree tramite l'elemento <slot>, quindi appare accanto al contenuto shadow anziché essere sostituito da esso.

Nota cosa fa e cosa non fa l'incapsulamento per gli stili. La regola .shadow-card vive all'interno del shadow tree e applica stili solo ai nodi in quell'albero; non può corrispondere a .card altrove nella pagina, e una regola .card nella pagina non può raggiungere il shadow tree. L'unica eccezione sono le proprietà ereditabilicolor, font-family, line-height e simili — che continuano a fluire attraverso il confine nel contenuto slottato. L'incapsulamento blocca la corrispondenza dei selettori, non l'ereditarietà. Per approfondire, vedi styling dello Shadow DOM e slot e composizione.

Quando utilizzare ciascuna tecnica

  • Usa <template> ogni volta che istanzi lo stesso markup ripetutamente (righe di elenco, schede, modal) e vuoi definirlo in modo dichiarativo in HTML.
  • Usa lo Shadow DOM quando un widget ha bisogno di stili che non devono collidere con la pagina host — un pulsante di un design system, un selettore di date, un widget incorporabile.
  • Combina entrambi — definisci il markup in un <template> e clonalo in una shadow root — per costruire elementi personalizzati completamente riutilizzabili.

Buone pratiche

  • Preferisci DocumentFragment per inserimenti in batch: Aggiungere un frammento a una shadow root (o a qualsiasi contenitore) in una singola operazione minimizza i ricalcoli del layout e migliora le prestazioni di rendering.
  • Popola i clone prima di inserirli: Esegui le query e popola il frammento clonato mentre è ancora staccato, così il browser esegue un solo reflow quando lo aggiungi.
  • Scegli la modalità shadow open: Mantiene i componenti debuggabili e testabili; closed non offre vera sicurezza.
  • Usa document.importNode() tra documenti diversi: Quando cloni contenuto da un altro documento o iframe, importNode garantisce la corretta proprietà dei nodi e previene errori cross-document.
  • Mantieni il light DOM minimale: Usa elementi <slot> per proiettare solo il contenuto che appartiene genuinamente alla pagina, mantenendo l'host prevedibile.
Informazione

Sfrutta lo Shadow DOM per incapsulare stili e funzionalità all'interno dei componenti, prevenendo conflitti di stile e garantendo codice modulare e manutenibile.

Conclusione

Le tecniche avanzate del DOM, come l'uso dei template e dello Shadow DOM, sono strumenti potenti per creare applicazioni web modulari, manutenibili ed efficienti. Incapsulando gli stili e i comportamenti dei componenti e utilizzando template riutilizzabili, puoi migliorare il tuo flusso di sviluppo e costruire applicazioni web robuste.

Esercizi

Pratica
Quali delle seguenti affermazioni sulle tecniche avanzate del DOM sono vere?
Quali delle seguenti affermazioni sulle tecniche avanzate del DOM sono vere?
Was this page helpful?