Element : méthode attachShadow()
Baseline
Large disponibilité
*
Cette fonctionnalité est bien établie et fonctionne sur de nombreux appareils et versions de navigateurs. Elle est disponible sur tous les navigateurs depuis janvier 2020.
* Certaines parties de cette fonctionnalité peuvent bénéficier de prise en charge variables.
La méthode attachShadow() de l'interface Element attache un arbre de DOM d'ombre à l'élément défini et retourne une référence à son ShadowRoot.
Syntaxe
attachShadow(options)
Paramètres
options-
Un objet qui contient les champs suivants :
mode-
Une chaîne de caractères définissant le mode d'encapsulation pour l'arbre de DOM d'ombre. Cela peut être l'une des valeurs suivantes :
open-
Les éléments à l'intérieur de la racine d'ombre sont accessibles depuis JavaScript avec la propriété
shadowRootde l'élément. closed-
Les éléments à l'intérieur de la racine d'ombre ne peuvent pas être accessibles depuis JavaScript avec la propriété
shadowRoot, qui est définie surnull.
clonableFacultatif-
Un booléen qui définit si la racine d'ombre peut être clonée : lorsqu'il est défini sur
true, l'élément hôte d'ombre cloné à l'aide deNode.cloneNode()ouDocument.importNode()inclut la racine d'ombre dans la copie. Sa valeur par défaut estfalse. customElementRegistryFacultatif-
Un objet
CustomElementRegistryqui est utilisé comme registre d'éléments personnalisés limité de la racine d'ombre attachée. Sinullouundefined, la racine d'ombre utilise le registre global référencé parWindow.customElements. delegatesFocusFacultatif-
Un booléen qui, lorsqu'il est défini sur
true, définit un comportement qui atténue les problèmes de focalisation des éléments personnalisés. Lorsqu'une partie non sélectionnable du DOM d'ombre est cliqué, la première partie sélectionnable reçoit la sélection, et l'hôte d'ombre reçoit tout style:focusdisponible. Sa valeur par défaut estfalse. referenceTargetFacultatif-
Une chaîne de caractères qui indique la cible effective de toute référence à un élément faite contre l'hôte d'ombre depuis l'extérieur de l'élément hôte. La valeur doit être l'ID d'un élément à l'intérieur du DOM d'ombre. Si elle est définie, les références à l'élément hôte depuis l'extérieur du DOM d'ombre font en sorte que l'élément cible référencé devienne la cible effective de la référence à l'élément hôte.
serializableFacultatif-
Un booléen qui, lorsqu'il est défini sur
true, indique que la racine d'ombre est mise en sérialisation. Si défini, la racine d'ombre peut être sérialisée en appelant les méthodesElement.getHTML()ouShadowRoot.getHTML()avec le paramètreoptions.serializableShadowRootsdéfini surtrue. Sa valeur par défaut estfalse. slotAssignmentFacultatif-
Une chaîne de caractères définissant le mode d'assignation des emplacements pour l'arbre DOM d'ombre. Cela peut être l'une des valeurs suivantes :
named-
Les éléments sont automatiquement assignés aux éléments HTML
<slot>dans cette racine d'ombre. Tous les enfants de premier niveau de l'hôte avec un attributslotcorrespondant à l'attributnamed'un<slot>dans cette racine d'ombre sont assignés à cet emplacement. Tous les enfants de premier niveau de l'hôte sans attributslotsont assignés au premier<slot>sans attributname(« l'emplacement par défaut »), si un tel emplacement est présent. C'est la valeur par défaut. manual-
Les éléments sont assignés manuellement à des éléments HTML
<slot>particuliers à l'aide deHTMLSlotElement.assign(). Aucune assignation automatique n'a lieu.
Valeur de retour
Retourne un objet ShadowRoot.
Exceptions
NotSupportedErrorDOMException-
Cette erreur peut être levée lorsque vous essayez d'attacher une racine d'ombre à un élément :
- en dehors de l'espace de noms HTML ou qui ne peut pas avoir d'ombre attachée.
- lorsque la propriété statique de définition de l'élément
disabledFeaturesa reçu la valeur"shadow". - qui a déjà une racine d'ombre qui n'a pas été créée de manière déclarative.
- qui a une racine d'ombre déclarative mais dont le
modedéfinit ne correspond pas au mode existant. - lors du passage d'une valeur
customElementRegistryqui n'est ninullni un registre à portée locale (que vous avez créé en utilisantnew CustomElementRegistry()). L'erreur est levée si vous passiez le registre global.
Description
La méthode Element.attachShadow() attache un arbre DOM d'ombre à l'élément défini et retourne une référence à sa racine d'ombre (ShadowRoot).
C'est le mécanisme programmatique pour créer un ShadowRoot, qui est le nœud racine d'un DOM d'ombre attaché à un élément hôte (il est également possible de créer un ShadowRoot de manière déclarative en utilisant l'attribut shadowrootmode de l'élément HTML <template>).
Il est utilisé pour créer des éléments personnalisés.
Éléments auxquels vous pouvez attacher un DOM d'ombre
Notez que vous ne pouvez pas attacher une racine d'ombre à tous les types d'éléments.
Il y en a certains qui ne peuvent pas avoir de DOM d'ombre pour des raisons de sécurité (par exemple <a>).
La liste suivante présente les éléments auxquels vous pouvez attacher une racine d'ombre :
Appeler cette méthode sur un élément qui est déjà un hôte d'ombre
Cette méthode peut être appelée sur un élément disposant déjà d'une racine d'ombre déclarative, à conditions que le mode définit dans mode corresponde au mode existant.
Dans ce cas, le ShadowRoot déjà présent est effacé puis retourné.
Cela permet de gérer les cas où, par exemple, le rendu côté serveur a déjà créé de manière déclarative une racine d'ombre, puis où le code côté client tente de rattacher à nouveau cette racine.
Dans le cas contraire, l'appel de attachShadow() sur un élément disposant déjà d'une racine d'ombre lève une exception.
Racines d'ombre ouvertes et fermées
Une racine d'ombre peut être attachée avec un mode d'encapsulation, qui est défini comme étant soit open (ouverte) soit closed (fermée).
Si l'argument {mode: "open"} est passé, la propriété shadowRoot de l'élément hôte peut ensuite être utilisée pour obtenir la racine d'ombre attachée.
Cela peut être utilisé pour accéder aux éléments dans le DOM d'ombre :
element.attachShadow({ mode: "open" });
element.shadowRoot; // Retourne un objet ShadowRoot
Si l'argument {mode: "closed"} est passé, la propriété shadowRoot de l'élément est définie sur null.
Notez que JavaScript peut toujours accéder à une racine d'ombre fermée en stockant la valeur retournée par la fonction.
element.attachShadow({ mode: "closed" });
element.shadowRoot; // Retourne null
Exemples
>Élément personnalisé de comptage de mots
L'exemple suivant est tiré de notre démo word-count-web-component (angl.) (à découvrir également en direct (angl.)).
Vous pouvez constater que nous utilisons attachShadow() au milieu du code pour créer une racine fantôme, à laquelle nous attachons ensuite le contenu de notre élément personnalisé.
// Crée une classe pour l'élément
class CompteurMot extends HTMLParagraphElement {
constructor() {
// Toujours appeler super en premier dans le constructeur
super();
// compter le nombre de mots dans l'élément parent de l'élément
const wcParent = this.parentNode;
function compteurMots(noeud) {
const texte = noeud.innerText || noeud.textContent;
return texte
.trim()
.split(/\s+/g)
.filter((a) => a.trim().length > 0).length;
}
const compte = `Mots : ${compteurMots(wcParent)}`;
// Crée une racine d'ombre
const ombre = this.attachShadow({ mode: "open" });
// Crée un nœud de texte et y ajoute le nombre de mots
const texte = document.createElement("span");
texte.textContent = compte;
// L'ajouter à la racine d'ombre
ombre.appendChild(texte);
// Met à jour le compte lorsque le contenu de l'élément change
this.parentNode.addEventListener("input", () => {
texte.textContent = `Mots : ${compteurMots(wcParent)}`;
});
}
}
// Définir le nouvel élément
customElements.define("compteur-mot", CompteurMot, { extends: "p" });
Désactiver le DOM d'ombre
Si l'élément a une propriété statique nommée fonctionnalitesDesactivees, qui est un tableau contenant la chaîne de caractères "shadow", alors l'appel à attachShadow() lève une exception.
Par exemple :
class MonElementPersonnalise extends HTMLElement {
// Désactiver le DOM d'ombre pour cet élément.
static fonctionnalitesDesactivees = ["shadow"];
constructor() {
super();
}
connectedCallback() {
// Crée une racine d'ombre.
// Cela lève une exception.
const ombre = this.attachShadow({ mode: "open" });
}
}
// Définir le nouvel élément
customElements.define("mon-element-personnalise", MonElementPersonnalise);
Attribuer un emplacement nommé
Cet exemple illustre l'attribution d'un emplacement nommé.
Créer un composant web
Ce code crée un composant web comportant trois emplacements nommés pour le titre, les métadonnées et le corps d'un article.
Le ShadowRoot est associé dans le constructeur de l'élément personnalisé.
Nous n'avons pas besoin de définir explicitement l'option slotAssignment: "named", car c'est la valeur par défaut.
class MonArticle extends HTMLElement {
constructor() {
super();
// Attacher la racine d'ombre
this.attachShadow({ mode: "open" /* , slotAssignment: "named" */ });
}
connectedCallback() {
this.render();
}
render() {
// Définir la structure interne et les styles
this.shadowRoot.innerHTML = `
<style>
.entete {
background-color: plum;
}
.meta {
background-color: green;
}
.corps {
background-color: lightblue;
}
</style>
<h2 class="entete">
<slot name="titre"></slot>
</h2>
<div class="meta">
<slot name="meta"></slot>
</div>
<div class="corps">
<slot></slot>
</div>
`;
}
}
// Définir le composant
customElements.define("mon-article", MonArticle);
Utiliser le composant web
Le HTML ci-dessous utilise le composant web <mon-article> que nous venons de créer.
Les éléments imbriqués sont rendus dans les emplacements du composant en fonction de la correspondance des noms.
Les éléments non nommés sont rendus dans l'emplacement non nommé (le corps).
<mon-article>
<span slot="titre">Texte pour l'emplacement du titre</span>
<span slot="meta">Texte pour l'emplacement des métadonnées</span>
<p>
Texte 1 sans attribut slot. Va dans l'emplacement par défaut (non nommé) à
l'intérieur d'un div « corps ».
</p>
<p>
Texte 2 sans attribut slot. Va également dans l'emplacement par défaut (non
nommé) à l'intérieur d'un div « corps ».
</p>
</mon-article>
Résultats
Cet exemple doit montrer le contenu des emplacements affichés dans les sections appropriées.
Attribuer un emplacement qui n'est pas nommé
Cet exemple montre l'attribution manuelle d'un emplacement.
Avec cette approche, chaque élément doit être attribué manuellement à un emplacement particulier à l'aide de HTMLSlotElement.assign().
Il n'y a pas d'attribution par défaut, donc tout emplacement non attribué est vide.
HTML
Tout d'abord, nous avons un avertissement de support caché, affiché avec JavaScript si le navigateur ne prend pas en charge slotAssignment: "manual".
<p id="support-warning" hidden>
⛔ Votre navigateur ne prend pas en charge l'attribution manuelle
d'emplacement (l'attribution nommée est utilisée).
</p>
Ensuite, nous définissons notre élément personnalisé <mon-article> avec des éléments enfants pour le titre, les métadonnées et le contenu du corps.
Chaque enfant est identifié par id ; contrairement à l'attribution d'emplacement nommée, aucun attribut slot n'est nécessaire.
<mon-article>
<span id="texte_titre">Texte pour l'emplacement du titre</span>
<span id="texte_meta">Texte pour l'emplacement des métadonnées</span>
<p id="texte_corps_1">Texte 1 pour l'emplacement du corps.</p>
<p id="texte_corps_2">Texte 2 pour l'emplacement du corps.</p>
</mon-article>
JavaScript
L'élément personnalisé attache une racine d'ombre avec slotAssignment: "manual".
Le DOM d'ombre contient des emplacements non nommés identifiés par id.
La méthode assignSlots() attribue manuellement les éléments du DOM visible aux emplacements.
Notez que plusieurs nœuds peuvent être attribués à un seul emplacement — l'ordre dans lequel ils sont définis contrôle l'ordre de rendu.
class MonArticle extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open", slotAssignment: "manual" });
}
connectedCallback() {
this.render();
this.assignSlots();
}
render() {
this.shadowRoot.innerHTML = `
<style>
.entete {
background-color: plum;
}
.meta {
background-color: green;
}
.corps {
background-color: lightblue;
}
</style>
<h2 class="entete">
<slot id="titreEmplacement"></slot>
</h2>
<div class="meta">
<slot id="metaEmplacement"></slot>
</div>
<div class="corps">
<slot id="corpsEmplacement"></slot>
</div>
`;
}
assignSlots() {
// 1. Cibler vos emplacements
const titreEmplacement = this.shadowRoot.querySelector("#titreEmplacement");
const metaEmplacement = this.shadowRoot.querySelector("#metaEmplacement");
const corpsEmplacement = this.shadowRoot.querySelector("#corpsEmplacement");
// 2. Cibler vos éléments du DOM visible
const texteTitre = this.querySelector("#texte_titre");
const texteMeta = this.querySelector("#texte_meta");
const texteCorps1 = this.querySelector("#texte_corps_1");
const texteCorps2 = this.querySelector("#texte_corps_2");
// 3. Attribuer manuellement les éléments
titreEmplacement.assign(texteTitre);
metaEmplacement.assign(texteMeta);
corpsEmplacement.assign(texteCorps2, texteCorps1);
}
}
customElements.define("mon-article", MonArticle);
Ce code teste si la propriété ShadowRoot.slotAssignment est définie et affiche l'avertissement si ce n'est pas le cas.
const isSlotAssignmentSupported = Object.hasOwn(
ShadowRoot.prototype,
"slotAssignment",
);
document
.querySelector("p[hidden]")
.toggleAttribute("hidden", isSlotAssignmentSupported);
Résultats
L'exemple ci-dessous doit afficher le contenu des emplacements dans les sections appropriées.
Note :
Si l'attribution manuelle des emplacements n'est pas prise en charge, un avertissement est affiché et le navigateur utilise l'attribution named.
Cependant, comme aucun des éléments du DOM clair n'a d'attribut slot, ils sont tous insérés dans le premier emplacement non nommé (l'emplacement du titre).
Spécifications
| Spécification |
|---|
| DOM> # dom-element-attachshadow> |
Compatibilité des navigateurs
Voir aussi
- La propriété
ShadowRoot.mode - La propriété
ShadowRoot.delegatesFocus - La propriété
ShadowRoot.slotAssignment - Attacher de manière déclarative une racine d'ombre avec l'attribut
shadowrootmodede l'élément<template> - DOM d'ombre déclaratif (angl.) sur web.dev (2023)