logo oujood
🔍

Shadow DOM : encapsuler le style et la structure de vos composants

Le Shadow DOM attache un arbre DOM privé à un élément HTML. Les styles CSS déclarés à l'intérieur restent confinés au composant — ils ne débordent pas sur la page, et les styles globaux n'y entrent pas. Ce tutoriel explique comment l'activer, le différence entre mode open et closed, et comment l'utiliser avec les Custom Elements.

OUJOOD.COM

Qu'est-ce que le Shadow DOM ?

JavaScript API – mise à jour 2026

Le Shadow DOM résout un problème concret : les conflits CSS. Quand votre page définit .titre { color: red; } et que votre composant définit aussi .titre, l'un écrase l'autre. Sans isolation, écrire des composants réutilisables dans une grande application devient rapidement un exercice de jonglage de spécificité.

Le Shadow DOM attache un arbre DOM privé à un élément HTML. Cet arbre — le shadow tree — a ses propres éléments et ses propres styles. Ce qui est dedans ne sort pas, ce qui est dehors n'entre pas. Point.

Ce n'est pas une nouveauté abstraite : le navigateur l'utilise depuis longtemps pour ses propres éléments. Ouvrez l'inspecteur sur un <input type="range">, activez l'option "Show user agent shadow DOM" dans DevTools — vous verrez le curseur, la piste et le bouton, tous construits avec un Shadow DOM que vous ne pouviez pas modifier jusqu'ici. En 2026, vous pouvez faire exactement la même chose pour vos propres composants.

Trois situations où le Shadow DOM change tout :

  • Conflits CSS : une règle .titre dans votre composant n'écrase jamais une règle .titre de la page principale.
  • Pollution du DOM global : les IDs et classes internes au composant sont invisibles depuis document.querySelector().
  • Maintenabilité : chaque composant porte son propre style — fini les conventions BEM pour éviter les collisions de noms.

Terminologie du Shadow DOM

Quatre termes reviennent dans toute la documentation sur le Shadow DOM. Autant les connaître avant de coder :

  • Shadow host : l'élément HTML ordinaire auquel on attache le Shadow DOM — par exemple votre <carte-info>.
  • Shadow root : la racine de l'arbre privé, retournée par attachShadow(). C'est sur ce nœud qu'on injecte le contenu.
  • Shadow tree : l'ensemble des nœuds DOM à l'intérieur du shadow root.
  • Light DOM : le DOM ordinaire de la page, visible dans l'inspecteur sans option spéciale.

Attacher un Shadow DOM avec attachShadow()

attachShadow() crée et attache un Shadow DOM à un élément en une seule ligne. Elle accepte un objet avec une propriété mode qui contrôle l'accessibilité du shadow root depuis l'extérieur.

Mode open

En mode open, le shadow root reste accessible depuis JavaScript via element.shadowRoot. C'est le mode à utiliser dans vos composants — il permet de debugger et de tester depuis l'extérieur.

  📋 Copier le code

const hote = document.querySelector('#mon-composant');

// Attacher un Shadow DOM en mode ouvert
const shadowRoot = hote.attachShadow({ mode: 'open' });

// Ajouter du contenu dans le shadow tree
shadowRoot.innerHTML = '<p>Contenu isolé dans le Shadow DOM.</p>';

// Accessible depuis l'extérieur en mode open
console.log(hote.shadowRoot); // Retourne le shadow root
  

Mode closed

En mode closed, element.shadowRoot retourne null — personne ne peut accéder au shadow tree depuis l'extérieur. C'est le mode que le navigateur utilise pour ses éléments natifs comme <video> ou <input type="range">. Pour vos propres composants, le mode open est généralement suffisant.

  📋 Copier le code

const hote = document.querySelector('#mon-composant');
const shadowRoot = hote.attachShadow({ mode: 'closed' });

shadowRoot.innerHTML = '<p>Contenu totalement privé.</p>';

// Inaccessible depuis l'extérieur en mode closed
console.log(hote.shadowRoot); // null
  

Encapsuler des styles CSS dans le Shadow DOM

Une balise <style> placée dans un shadow root ne s'applique qu'au contenu de ce shadow tree. Même règle dans l'autre sens : les styles globaux de la page n'y entrent pas. C'est une isolation à double sens — ni fuite vers l'extérieur, ni pollution depuis l'extérieur.

  📋 Copier le code

class BoutonStyled extends HTMLElement {
  connectedCallback() {
    const shadow = this.attachShadow({ mode: 'open' });

    shadow.innerHTML = `
      <style>
        /* Ce style n'affecte que l'intérieur du composant */
        button {
          background: #0066cc;
          color: white;
          border: none;
          padding: 0.6rem 1.4rem;
          border-radius: 6px;
          cursor: pointer;
          font-size: 1rem;
        }
        button:hover {
          background: #0052a3;
        }
      </style>
      <button>Cliquez-moi</button>
    `;
  }
}

customElements.define('bouton-styled', BoutonStyled);
  

Même si la page contient une règle CSS globale button { background: red; }, elle n'affectera pas le bouton à l'intérieur du Shadow DOM. L'isolation est totale.


Accéder au contenu du Shadow DOM

En mode open, on navigue dans le shadow tree via element.shadowRoot avec les méthodes DOM habituelles. Ce que document.querySelector() ne voit pas, element.shadowRoot.querySelector() le trouve sans problème.

  📋 Copier le code

const composant = document.querySelector('bouton-styled');

// document.querySelector ne voit pas l'intérieur du shadow DOM
console.log(document.querySelector('button')); // null

// Il faut passer par le shadow root
const bouton = composant.shadowRoot.querySelector('button');
console.log(bouton); // <button>Cliquez-moi</button>

// Modifier le contenu depuis l'extérieur
bouton.textContent = 'Merci !';
  

Shadow DOM et Custom Elements ensemble

Shadow DOM seul protège les styles. Custom Elements seul donne un nom de balise. Les deux ensemble produisent un composant autonome : balise personnalisée + style encapsulé + logique intégrée. On attache le shadow root dans le constructeur, puis on injecte HTML et CSS dans connectedCallback.

  📋 Copier le code

class CarteInfo extends HTMLElement {
  constructor() {
    super();
    // Attacher le shadow root dès la construction
    this._shadow = this.attachShadow({ mode: 'open' });
  }

  connectedCallback() {
    const titre = this.getAttribute('titre') || 'Titre';
    const texte = this.getAttribute('texte') || '';

    this._shadow.innerHTML = `
      <style>
        .carte {
          border: 1px solid #ddd;
          border-radius: 8px;
          padding: 1rem;
          font-family: sans-serif;
          max-width: 300px;
        }
        h3 { margin: 0 0 0.5rem; color: #0066cc; }
        p  { margin: 0; color: #444; font-size: 0.9rem; }
      </style>
      <div class="carte">
        <h3>${titre}</h3>
        <p>${texte}</p>
      </div>
    `;
  }
}

customElements.define('carte-info', CarteInfo);
  

Utilisation dans le HTML :

  📋 Copier le code

<carte-info titre="Shadow DOM" texte="Isolation totale du style et du DOM."></carte-info>
<carte-info titre="Custom Elements" texte="Vos propres balises HTML natives."></carte-info>
  

Récapitulatif

attachShadow({ mode: 'open' }) crée un arbre DOM privé dont les styles ne fuient pas vers la page et ne reçoivent rien d'elle. Mode open pour vos composants (debuggable), mode closed pour les éléments natifs du navigateur. Combiné aux Custom Elements, c'est la deuxième brique des Web Components. La troisième — Templates et Slots — vient compléter le mécanisme pour injecter du contenu dynamique dans vos composants.

Par carabde | Mis à jour le 17 avril 2026


chapitre précédent   sommaire   chapitre suivant