W3docs

Techniques DOM avancées

Maîtriser les techniques DOM avancées renforce vos compétences en développement web, en vous permettant de créer du code dynamique, modulaire et maintenable.

Maîtriser les techniques DOM avancées vous aide à créer des interfaces dynamiques, modulaires et maintenables en JavaScript pur — sans framework requis. Ce guide couvre deux piliers du travail avec les composants modernes : l'élément <template> pour définir des balises réutilisables et inertes, et le Shadow DOM pour encapsuler la structure, les styles et le comportement d'un composant. Ensemble, ils constituent la fondation qui propulse les web components et les custom elements.

Créer et utiliser des templates

Pourquoi les templates existent

Avant <template>, les développeurs créaient des balises réutilisables en plaçant du HTML dans des éléments <div> masqués, des littéraux de chaîne JavaScript ou des blocs <script type="text/template">. Chaque approche présente un inconvénient : les <div> masqués impliquent quand même un coût pour le navigateur en termes d'analyse et de chargement des ressources (les images se chargent, les scripts s'exécutent), et les templates sous forme de chaînes perdent la coloration syntaxique et sont faciles à casser.

L'élément <template> résout ce problème. Son contenu est analysé mais inerte : le navigateur construit les nœuds DOM mais ne les affiche pas, n'exécute pas leurs scripts, et ne charge pas leurs images ou médias tant que vous ne clonez pas explicitement le contenu dans le document actif. Cela fait de <template> l'outil idéal pour déclarer des balises destinées à être instanciées de nombreuses fois.

Utiliser l'élément <template>

L'élément <template> vous permet de définir du HTML qui n'est pas affiché lors du chargement de la page. Vous accédez à son contenu via la propriété en lecture seule content, qui retourne 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>

Cet exemple illustre la structure de base d'un élément <template> contenant une carte avec un titre et un contenu. Le contenu du template est cloné et inséré dans le DOM lorsque le bouton est cliqué. Pour approfondir l'élément en lui-même, consultez l'élément <template>.

Cloner et insérer le contenu d'un template

Pour réutiliser un <template>, clonez son content et insérez le clone dans le DOM. Passez toujours true à cloneNode afin que l'ensemble du sous-arbre (l'élément et tous ses descendants) soit copié — cloneNode(false) ne copie que le nœud supérieur et vous donnerait un fragment vide.

<!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>

La vraie valeur des templates réside dans ce modèle : cloner, puis peupler le clone avec des données avant de l'insérer. Quelques détails à retenir :

  • Un DocumentFragment se vide lui-même lors de l'ajout. Après appendChild(clone), les enfants du fragment se déplacent dans le conteneur et le fragment reste vide — appelez donc cloneNode une fois par élément que vous souhaitez ajouter.
  • Interrogez le clone, pas le document. Les sélecteurs comme clone.querySelector('.title') opèrent sur le fragment non encore inséré, ce qui vous permet de le remplir avant qu'il n'atteigne le DOM actif (évitant ainsi des reflows superflus). Voir la recherche avec querySelector.
  • document.importNode(template.content, true) est l'équivalent inter-documents — utilisez-le lorsque le template se trouve dans un autre document ou une iframe, afin que les nœuds importés appartiennent au document courant.

Shadow DOM

Introduction au Shadow DOM

Le Shadow DOM est un standard web qui permet l'encapsulation dans les web components. Il attache un arbre DOM séparé et caché — le shadow tree — à un élément (le shadow host). Les nœuds à l'intérieur de cet arbre ne sont pas accessibles par le document.querySelector normal de la page, et les styles définis à l'intérieur ne s'échappent pas vers le reste de la page. Cela maintient la structure interne, les styles et le comportement d'un composant isolés du document global.

Quelques termes que vous rencontrerez tout au long :

  • Shadow host — l'élément ordinaire auquel le shadow tree est attaché.
  • Shadow root — le nœud racine du shadow tree, retourné par attachShadow().
  • Shadow boundary — la limite entre le shadow tree et le reste du document que la portée ne traverse pas.

Mode ouvert vs. mode fermé

attachShadow() requiert une option 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)

En pratique, préférez open. Le mode closed ne fournit pas de véritable sécurité — n'importe qui peut surcharger attachShadow avant l'exécution de votre code — et il rend seulement votre composant plus difficile à tester et à déboguer.

Encapsulation et développement basé sur les composants

L'encapsulation garantit que les styles et scripts définis au sein d'un composant ne s'échappent pas pour affecter le reste du document — et que les styles extérieurs ne s'infiltrent pas à l'intérieur. L'exemple ci-dessous attache une shadow root, puis construit son contenu dans un DocumentFragment afin que l'ensemble du sous-arbre soit inséré en une seule opération. Un élément <slot> projette le contenu existant du host (le « light DOM ») dans le shadow tree aux côtés des propres balises du composant.

<!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>

Cet exemple crée un shadow tree sur #shadow-host et y injecte des styles et du contenu. Le contenu du light DOM (This is the light DOM content) reste dans le host et est exposé à l'intérieur du shadow tree via l'élément <slot>, de sorte qu'il apparaît aux côtés du contenu shadow plutôt que d'être remplacé par lui.

Notez ce que l'encapsulation fait et ne fait pas pour les styles. La règle .shadow-card vit à l'intérieur du shadow tree et ne s'applique qu'aux nœuds de cet arbre ; elle ne peut pas correspondre à .card ailleurs sur la page, et une règle .card dans la page ne peut pas atteindre l'intérieur du shadow tree. La seule exception concerne les propriétés héritablescolor, font-family, line-height et similaires — qui traversent toujours la frontière jusque dans le contenu slotté. L'encapsulation bloque la correspondance des sélecteurs, pas l'héritage. Pour approfondir le sujet, consultez le stylage du Shadow DOM et les slots et la composition.

Quand utiliser chaque technique

  • Utilisez <template> chaque fois que vous instanciez les mêmes balises de façon répétée (lignes de liste, cartes, modales) et souhaitez les définir de manière déclarative en HTML.
  • Utilisez le Shadow DOM lorsqu'un widget a besoin de styles qui ne doivent pas entrer en conflit avec la page hôte — un bouton de système de design, un sélecteur de date, un widget intégrable.
  • Combinez les deux — définissez les balises dans un <template> et clonez-les dans une shadow root — pour créer des custom elements réutilisables complets.

Bonnes pratiques

  • Préférez DocumentFragment pour les insertions par lots : Ajouter un fragment à une shadow root (ou tout autre conteneur) en une seule opération minimise les recalculs de mise en page et améliore les performances de rendu.
  • Peuplez les clones avant de les insérer : Interrogez et remplissez le fragment cloné tant qu'il est encore détaché, afin que le navigateur n'effectue qu'un seul reflow lors de l'ajout.
  • Choisissez le mode shadow open : Il maintient les composants déboguables et testables ; closed n'offre aucune véritable sécurité.
  • Utilisez document.importNode() entre documents : Lorsque vous clonez du contenu depuis un autre document ou une iframe, importNode garantit la propriété correcte des nœuds et prévient les erreurs inter-documents.
  • Gardez le light DOM minimal : Utilisez les éléments <slot> pour projeter uniquement le contenu qui appartient vraiment à la page, en maintenant le host prévisible.
Info

Exploitez le Shadow DOM pour encapsuler les styles et les fonctionnalités au sein des composants, évitant ainsi les conflits de styles et garantissant un code modulaire et maintenable.

Conclusion

Les techniques DOM avancées telles que l'utilisation des templates et du Shadow DOM sont des outils puissants pour créer des applications web modulaires, maintenables et efficaces. En encapsulant les styles et les comportements des composants et en utilisant des templates réutilisables, vous pouvez améliorer votre flux de travail de développement et créer des applications web robustes.

Entraînement

Pratique
Lesquelles des affirmations suivantes concernant les techniques DOM avancées sont vraies ?
Lesquelles des affirmations suivantes concernant les techniques DOM avancées sont vraies ?
Was this page helpful?