[ Web Proxy ]
URL:
Viewing: https://developer.mozilla.org/fr/docs/Web/API/Web_components/Using_shadow_DOM [Back]  [Original]

Utiliser le DOM d'ombre - Les API Web | MDN

Cette page a t traduite partir de l'anglais par la communaut. Vous pouvez contribuer en rejoignant la communaut francophone sur MDN Web Docs.

View in English Always switch to English

Utiliser le DOM d'ombre

Un aspect important des lments personnaliss est l'encapsulation, car un lment personnalis, par dfinition, est une fonctionnalit rutilisable : il peut tre insr dans n'importe quelle page web et on s'attend ce qu'il fonctionne. Il est donc important que le code excut dans la page ne puisse pas accidentellement casser un lment personnalis en modifiant son implmentation interne. Le DOM d'ombre (shadow DOM en anglais) permet d'associer un arbre DOM un lment, et d'avoir l'intrieur de cet arbre inaccessible au JavaScript et au CSS excuts dans la page.

Cet article prsente les bases de l'utilisation du DOM d'ombre.

Dans cet article

Vue de haut niveau

Cet article suppose que vous tes dj familier avec le concept de DOM (Document Object Model) une structure arborescente de nuds connects reprsentant les diffrents lments et chanes de caractres apparaissant dans un document balis (gnralement un document HTML dans le cas de documents web). Par exemple, considrez le fragment HTML suivant :

html
<html lang="fr">
  <head>
    <meta charset="utf-8" />
    <title>Simple exemple de DOM</title>
  </head>
  <body>
    <section>
      <img
        src="dinosaur.png"
        alt="Un tyrannosaurus Rex rouge&nbsp;: un dinosaure bipde se tenant debout comme un humain, avec de petits bras et une large gueule  nombreuses dents tranchantes." />
      <p>
        Nous ajouterons ici un lien vers la
        <a href="https://www.mozilla.org/">page d'accueil de Mozilla</a>
      </p>
    </section>
  </body>
</html>

Ce fragment produit la structure DOM suivante :

- HTML
    - HEAD
        - META charset="utf-8"
        - TITLE
            - #text: Simple exemple de DOM
    - BODY
        - SECTION
            - IMG src="dinosaur.png" alt="Un tyrannosaurus Rex rouge : un dinosaure bipde se tenant debout comme un humain, avec de petits bras et une large gueule  nombreuses dents tranchantes."
            - P
                - #text: Ici, nous ajouterons un lien vers la
                - A href="https://www.mozilla.org/"
                    - #text: page d'accueil de Mozilla

Le DOM d'ombre permet d'associer des arbres DOM cachs des lments dans l'arbre DOM principal : cet arbre DOM d'ombre commence par une racine d'ombre, sous laquelle vous pouvez attacher n'importe quel lment, comme dans le DOM classique.

Version SVG du schma montrant l'interaction entre le document, la racine d'ombre et l'hte d'ombre. [Version SVG du schma montrant l'interaction entre le document, la racine d'ombre et l'hte d'ombre.]

Il existe quelques termes spcifiques au DOM d'ombre connatre :

  • Hte d'ombre : le nud DOM classique auquel le DOM d'ombre est attach.
  • Arbre d'ombre : l'arbre DOM l'intrieur du DOM d'ombre.
  • Limite d'ombre : l'endroit o le DOM d'ombre se termine et o le DOM classique commence.
  • Racine d'ombre : le nud racine de l'arbre d'ombre.

Vous pouvez manipuler les nuds dans le DOM d'ombre exactement comme les nuds classiques : par exemple, en ajoutant des enfants ou en dfinissant des attributs, en mettant en forme des nuds individuels avec element.style.foo, ou en ajoutant du style tout l'arbre DOM d'ombre l'intrieur d'un lment <style>. La diffrence est qu'aucun code l'intrieur d'un DOM d'ombre ne peut affecter quoi que ce soit l'extrieur, ce qui permet une encapsulation pratique.

Avant que le DOM d'ombre ne soit mis disposition des dveloppeureuses web, les navigateurs l'utilisaient dj pour encapsuler la structure interne d'un lment. Pensez par exemple un lment <video>, avec les contrles par dfaut du navigateur affichs. Tout ce que vous voyez dans le DOM est l'lment <video>, mais il contient une srie de boutons et d'autres contrles l'intrieur de son DOM d'ombre. La spcification du DOM d'ombre vous permet de manipuler le DOM d'ombre de vos propres lments personnaliss.

Hritage des attributs

L'arbre d'ombre et les lments <slot> hritent des attributs dir et lang de leur hte d'ombre.

Cration d'un DOM d'ombre

De manire imprative avec JavaScript

La page suivante contient deux lments, un lment HTML <div> avec un id de "host", et un lment HTML <span> contenant du texte :

html
<div id="host"></div>
<span>Je ne suis pas dans le DOM d'ombre</span>

Nous allons utiliser l'lment "host" comme hte d'ombre. Nous appelons attachShadow() sur l'hte pour crer le DOM d'ombre, puis nous pouvons ajouter des nuds au DOM d'ombre comme nous le ferions dans le DOM principal. Dans cet exemple, nous ajoutons un seul lment <span> :

js
const host = document.querySelector("#host");
const shadow = host.attachShadow({ mode: "open" });
const span = document.createElement("span");
span.textContent = "Je suis dans le DOM d'ombre";
shadow.appendChild(span);

Le rsultat ressemble ceci :

De manire dclarative avec HTML

Crer un DOM d'ombre via l'API JavaScript peut tre une bonne option pour les applications rendues ct client. Pour d'autres applications, une interface utilisateur rendue ct serveur peut offrir de meilleures performances et donc une meilleure exprience utilisateur. Dans ces cas, vous pouvez utiliser l'lment <template> pour dfinir le DOM d'ombre de manire dclarative. La cl de ce comportement est l'attribut numr shadowrootmode, qui peut tre dfini sur open ou closed, les mmes valeurs que l'option mode de la mthode attachShadow().

html
<div id="host">
  <template shadowrootmode="open">
    <span>Je suis dans le DOM d'ombre</span>
  </template>
</div>

Note : Par dfaut, le contenu de <template> n'est pas affich. Dans ce cas, puisque shadowrootmode="open" a t inclus, la racine d'ombre est rendue. Dans les navigateurs compatibles, le contenu visible l'intrieur de cette racine d'ombre est affich.

Aprs que le navigateur a analys le HTML, il remplace l'lment <template> par son contenu envelopp dans une racine d'ombre qui est attache l'lment parent, le <div id="host"> dans notre exemple. L'arbre DOM rsultant ressemble ceci (il n'y a pas d'lment <template> dans l'arbre DOM) :

- DIV id="host"
  - #shadow-root
    - SPAN
      - #text: Je suis dans le DOM d'ombre

Notez qu'en plus de shadowrootmode, vous pouvez utiliser des attributs de <template> tels que shadowrootclonable et shadowrootdelegatesfocus pour prciser d'autres proprits de la racine d'ombre gnre.

Encapsulation depuis JavaScript

Cela peut paratre peu spectaculaire pour l'instant. Voyons ce qui se passe si le code s'excutant dans la page tente d'accder aux lments du DOM d'ombre.

Cette page est identique la prcdente, sauf que nous avons ajout deux lments <button>.

html
<div id="host"></div>
<span>Je ne suis pas dans le DOM d'ombre</span>
<br />

<button id="upper" type="button">Mettre en majuscules les lments span</button>
<button id="reload" type="button">Recharger</button>

Cliquer sur le bouton  Mettre en majuscules les lments span  recherche tous les lments <span> de la page et transforme leur texte en majuscules. Cliquer sur  Recharger  recharge simplement la page, pour ressayer.

js
const host = document.querySelector("#host");
const shadow = host.attachShadow({ mode: "open" });
const span = document.createElement("span");
span.textContent = "Je suis dans le DOM d'ombre";
shadow.appendChild(span);

const upper = document.querySelector("button#upper");
upper.addEventListener("click", () => {
  const spans = Array.from(document.querySelectorAll("span"));
  for (const span of spans) {
    span.textContent = span.textContent.toUpperCase();
  }
});

const reload = document.querySelector("#reload");
reload.addEventListener("click", () => document.location.reload());

Si vous cliquez sur  Mettre en majuscules les lments span , vous verrez que Document.querySelectorAll() ne trouve pas les lments dans notre DOM d'ombre : ils sont effectivement invisibles pour le JavaScript de la page.

Element.shadowRoot et l'option mode

Dans l'exemple cidessous, nous passons l'argument { mode: "open" } attachShadow(). Lorsque mode est dfini sur "open", le JavaScript de la page peut accder l'intrieur de votre DOM d'ombre via la proprit shadowRoot de l'hte d'ombre.

Dans cet exemple, comme prcdemment, le HTML contient l'hte d'ombre, un <span> dans l'arbre DOM principal et deux boutons :

html
<div id="host"></div>
<span>Je ne suis pas dans le DOM d'ombre</span>
<br />

<button id="upper" type="button">
  Mettre en majuscules les lments span du DOM d'ombre
</button>
<button id="reload" type="button">Recharger</button>

Cette fois, le bouton  Mettre en majuscules  utilise shadowRoot pour trouver les <span> dans le DOM :

js
const host = document.querySelector("#host");
const shadow = host.attachShadow({ mode: "open" });
const span = document.createElement("span");
span.textContent = "Je suis dans le DOM d'ombre";
shadow.appendChild(span);

const upper = document.querySelector("button#upper");
upper.addEventListener("click", () => {
  const spans = Array.from(host.shadowRoot.querySelectorAll("span"));
  for (const span of spans) {
    span.textContent = span.textContent.toUpperCase();
  }
});

const reload = document.querySelector("#reload");
reload.addEventListener("click", () => document.location.reload());

Cette fois, le JavaScript de la page peut accder aux lments internes du DOM d'ombre :

L'argument {mode: "open"} offre la page un moyen de rompre l'encapsulation de votre DOM d'ombre. Si vous ne souhaitez pas donner cette possibilit, passez {mode: "closed"} et shadowRoot renverra null.

Cependant, cela ne doit pas tre considr comme un mcanisme de scurit solide, car il existe des moyens de le contourner, par exemple via des extensions de navigateur s'excutant dans la page. Il s'agit plutt d'une indication que la page ne doit pas accder l'implmentation interne de votre arbre d'ombre.

Encapsulation depuis le CSS

Dans cette version de la page, le HTML est identique l'original :

html
<div id="host"></div>
<span>Je ne suis pas dans le DOM d'ombre</span>

En JavaScript, nous crons le DOM d'ombre :

js
const host = document.querySelector("#host");
const shadow = host.attachShadow({ mode: "open" });
const span = document.createElement("span");
span.textContent = "Je suis dans le DOM d'ombre";
shadow.appendChild(span);

Cette fois, nous avons du CSS ciblant les lments <span> de la page :

css
span {
  color: blue;
  border: 1px solid black;
}

Le CSS de la page n'affecte pas les nuds situs l'intrieur du DOM d'ombre :

Appliquer des styles dans le DOM d'ombre

Dans cette section, nous examinons deux faons d'appliquer des styles dans un arbre de DOM d'ombre :

Dans les deux cas, les styles dfinis dans l'arbre du DOM d'ombre sont limits cet arbre : tout comme les styles de la page n'affectent pas les lments du DOM d'ombre, les styles du DOM d'ombre n'affectent pas les lments du reste de la page.

Feuilles de style constructibles

Pour mettre en forme des lments dans le DOM d'ombre l'aide de feuilles de style constructibles, nous pouvons :

  1. Crer un objet CSSStyleSheet vide
  2. Dfinir son contenu en utilisant CSSStyleSheet.replace() ou CSSStyleSheet.replaceSync()
  3. L'ajouter la racine d'ombre en l'assignant ShadowRoot.adoptedStyleSheets

Les rgles dfinies dans la CSSStyleSheet seront limites l'arbre du DOM d'ombre, ainsi qu' tout autre arbre DOM auquel nous l'avons assigne.

Ici, encore une fois, le HTML contient notre hte et un <span> :

html
<div id="host"></div>
<span>Je ne suis pas dans le DOM d'ombre</span>

Cette fois, nous crons le DOM d'ombre et lui assignons un objet CSSStyleSheet :

js
const sheet = new CSSStyleSheet();
sheet.replaceSync("span { color: red; border: 2px dotted black;}");

const host = document.querySelector("#host");

const shadow = host.attachShadow({ mode: "open" });
shadow.adoptedStyleSheets = [sheet];

const span = document.createElement("span");
span.textContent = "Je suis dans le DOM d'ombre";
shadow.appendChild(span);

Les styles dfinis dans l'arbre du DOM d'ombre ne s'appliquent pas au reste de la page :

Ajouter des lments <style> dans des dclarations <template>

Une alternative la construction d'objets CSSStyleSheet consiste inclure un lment <style> l'intrieur de l'lment <template> utilis pour dfinir un composant Web.

Dans ce cas, le HTML inclut la dclaration <template> :

html
<template id="my-element">
  <style>
    span {
      color: red;
      border: 2px dotted black;
    }
  </style>
  <span>Je suis dans le DOM d'ombre</span>
</template>

<div id="host"></div>
<span>Je ne suis pas dans le DOM d'ombre</span>

En JavaScript, nous crons le DOM d'ombre et ajoutons le contenu du <template> :

js
const host = document.querySelector("#host");
const shadow = host.attachShadow({ mode: "open" });
const template = document.getElementById("my-element");

shadow.appendChild(template.content);

Encore une fois, les styles dfinis dans le <template> ne s'appliquent qu' l'intrieur de l'arbre du DOM d'ombre, et pas au reste de la page :

Choisir entre les options programmatiques et dclaratives

Le choix entre ces options dpend de votre application et de vos prfrences personnelles.

Crer un objet CSSStyleSheet et l'assigner la racine d'ombre via adoptedStyleSheets permet de crer une seule feuille de style et de la partager entre plusieurs arbres DOM. Par exemple, une bibliothque de composants peut crer une seule feuille de style et la partager entre tous les lments personnaliss de cette bibliothque. Le navigateur analysera cette feuille une seule fois. De plus, vous pouvez modifier dynamiquement la feuille de style et propager ces changements tous les composants qui l'utilisent.

L'approche consistant attacher un lment <style> est idale si vous souhaitez tre dclaratif, que vous avez peu de styles et que vous n'avez pas besoin de partager les styles entre diffrents composants.

DOM d'ombre et lments personnaliss

Sans l'encapsulation fournie par le DOM d'ombre, les lments personnaliss seraient extrmement fragiles. Il serait trop facile pour une page de casser accidentellement le comportement ou la mise en page d'un lment personnalis en excutant du JavaScript ou du CSS sur la page. En tant que dveloppeureuse d'lments personnaliss, vous ne sauriez jamais si les slecteurs applicables l'intrieur de votre lment entraient en conflit avec ceux d'une page qui utilise votre lment personnalis.

Les lments personnaliss sont implments comme une classe qui tend soit la base HTMLElement, soit un lment HTML natif tel que HTMLParagraphElement. En gnral, l'lment personnalis lui-mme est un hte d'ombre, et l'lment cre plusieurs lments sous cette racine pour fournir l'implmentation interne de l'lment.

L'exemple ci-dessous cre un lment personnalis <filled-circle> qui affiche simplement un cercle rempli d'une couleur unie.

js
class FilledCircle extends HTMLElement {
  constructor() {
    super();
  }
  connectedCallback() {
    // Crer une racine d'ombre
    // L'lment personnalis lui-mme est l'hte d'ombre
    const shadow = this.attachShadow({ mode: "open" });

    // crer l'implmentation interne
    const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    const circle = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "circle",
    );
    circle.setAttribute("cx", "50");
    circle.setAttribute("cy", "50");
    circle.setAttribute("r", "50");
    circle.setAttribute("fill", this.getAttribute("color"));
    svg.appendChild(circle);

    shadow.appendChild(svg);
  }
}

customElements.define("filled-circle", FilledCircle);
html
<filled-circle color="blue"></filled-circle>

Pour d'autres exemples illustrant diffrents aspects de l'implmentation d'lments personnaliss, consultez notre guide sur les lments personnaliss.

Voir aussi


Web Proxy Viewer  |  New URL  |  Original Page