[Conseiller la lecture] Les 19 autres parties du cycle Aujourd'hui, dans la traduction de 17 parties des documents consacrés aux fonctionnalités de tout ce qui est en quelque sorte lié à JavaScript, nous parlerons des composants Web et des différentes normes qui visent à travailler avec eux. Une attention particulière sera portée à la technologie Shadow DOM.

Revue
Les composants Web sont une famille d'API conçues pour décrire de nouveaux éléments DOM pouvant être réutilisés. La fonctionnalité de ces éléments est séparée du reste du code; ils peuvent être utilisés dans des applications Web de notre propre conception.
Il existe quatre technologies liées aux composants Web:
- DOM fantôme (DOM fantôme)
- Modèles HTML (modèles HTML)
- Éléments personnalisés
- Importations HTML (importation HTML)
Dans cet article, nous parlerons de la technologie Shadow DOM, conçue pour créer des applications basées sur des composants. Il offre des moyens de résoudre les problèmes de développement Web courants que vous avez peut-être déjà rencontrés:
- Isolement DOM: le composant a une arborescence DOM isolée (cela signifie que la commande
document.querySelector()
ne permettra pas l'accès au nœud dans le DOM fantôme du composant). De plus, il simplifie le système de sélecteur CSS dans les applications Web, car les composants DOM sont isolés, ce qui permet au développeur d'utiliser les mêmes identificateurs universels et noms de classe dans différents composants sans se soucier d'éventuels conflits de noms. - Isolation CSS: les règles CSS décrites dans le DOM fantôme y sont limitées. Ces styles ne quittent pas l'élément, ils ne se mélangent pas avec d'autres styles de page.
- Composition: Développement d'une API déclarative pour les composants basés sur le balisage.
Shadow DOM Technology
Il suppose que vous connaissez déjà le concept du DOM et les API associées. Si ce n'est pas le cas, vous pouvez lire
ce document.
Le DOM fantôme est fondamentalement le même qu'un DOM ordinaire, mais avec deux différences:
- La première est de savoir comment le Shadow DOM est créé et utilisé, en particulier, il s'agit de la relation du Shadow DOM avec le reste de la page.
- Le second est le comportement du DOM Shadow par rapport à la page.
Lorsque vous travaillez avec le DOM, des nœuds DOM sont créés qui se joignent, en tant qu'enfants, à d'autres éléments de la page. Dans le cas de la technologie Shadow DOM, une arborescence DOM isolée est créée qui joint l'élément, mais elle est séparée de ses éléments enfants normaux.
Ce sous-arbre isolé s'appelle l'arbre d'ombre. L'élément auquel un tel arbre est attaché est appelé hôte fantôme. Tout ce qui est ajouté à la sous-arborescence DOM fantôme se révèle être local à l'élément auquel il est attaché, y compris les styles décrits à l'aide de balises
<style>
. C'est ainsi que l'isolement CSS est assuré par la technologie Shadow DOM.
Création d'un DOM fantôme
La racine fantôme est une partie du document qui s'attache à l'élément hôte. Un élément acquiert un DOM fantôme lorsqu'un élément racine fantôme lui est attaché. Afin de créer un DOM fantôme pour un certain élément, vous devez utiliser une commande de la forme
element.attachShadow()
:
var header = document.createElement('header'); var shadowRoot = header.attachShadow({mode: 'open'}); shadowRoot.appendChild(document.createElement('<p> Shadow DOM </p>');
Il convient de noter que dans
la spécification Shadow DOM, il existe une liste d'éléments auxquels les sous-arbres DOM ne peuvent pas être connectés.
Composition dans Shadow DOM
La composition est l'une des fonctionnalités les plus importantes du Shadow DOM, c'est un moyen de créer des applications Web, qui est utilisé dans le processus d'écriture de code HTML. Au cours de ce processus, le programmeur combine les différents éléments constitutifs (éléments) qui composent la page, les imbriquant, si nécessaire, les uns dans les autres. Par exemple, ce sont des éléments tels que
<div>
,
<header>
,
<form>
et d'autres utilisés pour créer des interfaces d'application Web, y compris celles qui agissent comme des conteneurs pour d'autres éléments.
La composition détermine la capacité d'éléments, tels que
<select>
,
<form>
,
<video>
, à inclure d'autres éléments HTML en tant qu'enfants, et la capacité d'organiser le comportement spécial de telles structures composées de différents éléments.
Par exemple, l'élément
<select>
possède des moyens pour rendre les éléments
<option>
sous la forme d'une liste déroulante avec le contenu prédéterminé des éléments d'une telle liste.
Considérez certaines des fonctionnalités du DOM Shadow utilisées dans la composition des éléments.
Dom léger
Light DOM est le balisage créé par l'utilisateur de votre composant. Ce DOM est en dehors du DOM fantôme du composant et est un enfant du composant. Imaginez que vous avez créé un composant personnalisé appelé
<better-button>
qui étend les capacités de l'élément HTML
<button>
standard, et que l'utilisateur doit ajouter une image et du texte à ce nouvel élément. Voici à quoi ça ressemble:
<extended-button> <img align="center" src="boot.png" slot="image"> <span>Launch</span> </extended-button>
L'élément
<extended-button>
est un composant personnalisé décrit par le programmeur seul, et le code HTML à l'intérieur de ce composant est son Light DOM - ce que l'utilisateur de ce composant y a ajouté.
Le DOM fantôme dans cet exemple est le composant
<extended-button>
. Il s'agit d'un modèle d'objet local d'un composant qui décrit sa structure interne, isolée du monde extérieur de CSS, et encapsule les détails d'implémentation du composant.
Dom aplati
L'arborescence DOM aplatie représente la façon dont le navigateur affiche le composant à l'écran, combinant le DOM léger et le DOM ombre. C'est une telle arborescence DOM qui peut être vue dans les outils de développement, et c'est elle qui est affichée sur la page. Cela peut ressembler à ceci:
<extended-button> #shadow-root <style>…</style> <slot name="image"> <img align="center" src="boot.png" slot="image"> </slot> <span id="container"> <slot> <span>Launch</span> </slot> </span> </extended-button>
Patterns
Si vous devez constamment utiliser les mêmes structures dans le balisage HTML des pages Web, il sera utile d'utiliser un certain modèle au lieu d'écrire encore et encore le même code. Cela était possible auparavant, mais maintenant tout a été considérablement simplifié grâce à l'apparition de la balise HTML
<template>
, qui bénéficie d'un excellent support pour les navigateurs modernes. Cet élément et son contenu ne sont pas affichés dans le DOM, mais vous pouvez travailler avec lui à partir de JavaScript. Prenons un exemple simple:
<template id="my-paragraph"> <p> Paragraph content. </p> </template>
Si vous incluez cette conception dans le balisage HTML de la page, le contenu de la
<p>
décrit n'apparaîtra pas à l'écran tant qu'il ne sera pas explicitement attaché au DOM du document. Par exemple, cela pourrait ressembler à ceci:
var template = document.getElementById('my-paragraph'); var templateContent = template.content; document.body.appendChild(templateContent);
Il existe d'autres moyens pour obtenir le même effet, mais, comme déjà mentionné, les modèles sont un outil standard très pratique qui bénéficie d'une bonne prise en charge du navigateur.
Prise en charge du navigateur HTML pour les navigateurs modernesLes modèles sont utiles en eux-mêmes, mais leurs capacités sont entièrement divulguées lorsqu'ils sont utilisés avec des éléments personnalisés. Les éléments personnalisés sont un sujet pour un matériau distinct, et maintenant, pour comprendre ce qui se passe, il suffit de tenir compte du fait que l'
customElement
navigateurs
customElement
permet au programmeur de décrire leurs propres balises HTML et de spécifier à quoi ressembleront les éléments créés avec ces balises à l'écran.
Définissez un composant Web qui utilise notre modèle comme contenu pour son DOM fantôme. Appelez ce nouvel élément
<my-paragraph>
:
customElements.define('my-paragraph', class extends HTMLElement { constructor() { super(); let template = document.getElementById('my-paragraph'); let templateContent = template.content; const shadowRoot = this.attachShadow({mode: 'open'}).appendChild(templateContent.cloneNode(true)); } });
La chose la plus importante à prendre en compte est que nous avons attaché un clone du contenu du modèle créé à l'aide de la méthode
Node.cloneNode () à la racine fantôme.
Puisque nous attachons le contenu du modèle au DOM fantôme, nous pouvons inclure des informations de style dans le modèle dans l'élément
<style> , qui seront ensuite encapsulées dans l'élément utilisateur. Ce schéma ne fonctionnera pas comme prévu si vous travaillez avec le DOM normal au lieu du DOM fantôme.
Par exemple, un modèle peut être modifié comme suit en y incluant des informations de style:
<template id="my-paragraph"> <style> p { color: white; background-color: #666; padding: 5px; } </style> <p>Paragraph content. </p> </template>
Maintenant, l'élément utilisateur que nous décrivons peut être utilisé sur les pages Web ordinaires comme suit:
<my-paragraph></my-paragraph>
Machines à sous
Les modèles HTML présentent plusieurs inconvénients, le principal étant que les modèles contiennent un balisage statique, ce qui ne permet pas, par exemple, d'afficher le contenu de certaines variables avec leur aide afin de travailler avec elles de la même manière qu'avec le HTML standard modèles. C'est là
<slot>
balise
<slot>
.
Les emplacements peuvent être perçus comme des espaces réservés qui vous permettent d'inclure votre propre code HTML dans le modèle. Cela vous permet de créer des modèles HTML universels, puis de les rendre personnalisables en leur ajoutant des emplacements.
Jetez un œil à l'apparence du modèle ci-dessus en utilisant la
<slot>
:
<template id="my-paragraph"> <p> <slot name="my-text">Default text</slot> </p> </template>
Si le contenu de l'emplacement n'est pas spécifié lorsque l'élément est inclus dans le balisage, ou si le navigateur ne prend pas en charge l'utilisation des emplacements, l'élément
<my-paragraph>
inclura uniquement le contenu standard du
Default text
.
Afin de définir le contenu de l'emplacement, vous devez inclure du code HTML avec l'attribut
slot
dans l'élément
<my-paragraph>
, dont la valeur est équivalente au nom de l'emplacement dans lequel vous souhaitez placer ce code.
Comme auparavant, il peut y avoir n'importe quoi. Par exemple:
<my-paragraph> <span slot="my-text">Let's have some different text!</span> </my-paragraph>
Les éléments qui peuvent être placés dans des emplacements sont appelés éléments
insérables .
Veuillez noter que dans l'exemple précédent, nous avons ajouté l'élément
<span>
à l'emplacement, il s'agit de ce que l'on appelle l'élément fendu. Il a un attribut d'
slot
auquel est affectée la valeur
my-text
, c'est-à-dire la même valeur que celle utilisée dans l'attribut de
name
de l'emplacement décrit dans le modèle.
Après avoir traité le balisage ci-dessus, le navigateur créera l'arborescence DOM aplatie suivante:
<my-paragraph> #shadow-root <p> <slot name="my-text"> <span slot="my-text">Let's have some different text!</span> </slot> </p> </my-paragraph>
Faites attention à l'élément
#shadow-root
. Ceci est juste un indicateur de l'existence du DOM Shadow.
Stylisation
Les composants qui utilisent la technologie Shadow DOM peuvent être stylisés sur une base commune, ils peuvent définir leurs propres styles ou fournir des crochets sous la forme de
propriétés CSS personnalisées qui permettent aux utilisateurs de composants de remplacer les styles par défaut.
▍ Styles décrits dans les composants
L'isolement CSS est l'une des fonctionnalités les plus remarquables de la technologie Shadow DOM. À savoir, nous parlons de ce qui suit:
- Les sélecteurs CSS de la page sur laquelle le composant correspondant est placé n'affectent pas ce qu'il contient.
- Les styles décrits dans le composant n'affectent pas la page. Ils sont isolés dans l'élément hôte.
Les sélecteurs CSS utilisés dans le DOM fantôme s'appliquent localement au contenu du composant. En pratique, cela signifie la possibilité de réutiliser les mêmes identificateurs et noms de classe dans différents composants et pas besoin de s'inquiéter des conflits de noms. Les sélecteurs CSS simples signifient également de meilleures performances pour les solutions dans lesquelles ils sont utilisés.
Jetez un œil à l'élément
#shadow-root
, qui définit certains styles:
#shadow-root <style> #container { background: white; } #container-items { display: inline-flex; } </style> <div id="container"></div> <div id="container-items"></div>
Tous les styles ci-dessus sont locaux à
#shadow-root
.
De plus, vous pouvez utiliser la
<link>
pour inclure des feuilles de style externes dans
#shadow-root
. Ces styles seront également locaux.
▍Pseudoclasse: hôte
La pseudo-
:host
vous permet d'accéder à un élément contenant une arborescence DOM fantôme et de styliser cet élément:
<style> :host { display: block; } </style>
En utilisant la
:host
pseudo-
:host
, rappelez-vous que les règles de la page parent ont une priorité plus élevée que celles qui sont spécifiées dans l'élément utilisant cette pseudo-classe. Cela permet aux utilisateurs de remplacer les styles de composant hôte qui y sont définis de l'extérieur. De plus, la pseudo-
:host
ne fonctionne que dans le contexte de l'élément root shadow, vous ne pouvez pas l'utiliser en dehors de l'arborescence DOM shadow.
La forme fonctionnelle de la pseudo-classe ,:
:host(<selector>)
, vous permet d'accéder à l'élément host s'il correspond à l'élément
<selector>
spécifié. C'est un excellent moyen pour permettre aux composants d'encapsuler un comportement qui répond aux actions de l'utilisateur ou aux changements d'état d'un composant, et vous permet de styliser les nœuds internes en fonction du composant hôte:
<style> :host { opacity: 0.4; } :host(:hover) { opacity: 1; } :host([disabled]) { background: grey; pointer-events: none; opacity: 0.4; } :host(.pink) > #tabs { color: pink; } </style>
▍Topiques et éléments avec une pseudo-classe: host-context (<selector>)
La pseudo-
:host-context(<selector>)
correspond à l'élément host si lui ou l'un de ses ancêtres correspond à l'élément
<selector>
spécifié.
Un cas d'utilisation courant pour cette fonctionnalité est de styliser des éléments avec des thèmes. Par exemple, les thèmes sont souvent utilisés en affectant la classe appropriée aux balises
<html>
ou
<body>
:
<body class="lightheme"> <custom-container> … </custom-container> </body>
La pseudo-
:host-context(.lightheme)
sera appliquée à
<fancy-tabs>
si cet élément est un descendant de
.lightteme
:
:host-context(.lightheme) { color: black; background: white; }
La construction
:host-context()
peut être utile pour appliquer des thèmes, mais à cette fin, il est préférable d'utiliser des crochets à l'aide de
propriétés CSS personnalisées .
▍ Styliser l'élément hôte du composant de l'extérieur
L'élément hôte du composant peut être stylisé en externe en utilisant le nom de sa balise comme sélecteur:
custom-container { color: red; }
Les styles externes ont priorité sur les styles définis dans le DOM fantôme.
Supposons qu'un utilisateur crée le sélecteur suivant:
custom-container { width: 500px; }
Il remplacera la règle définie dans le composant lui-même:
:host { width: 300px; }
En utilisant cette approche, vous pouvez styliser uniquement le composant lui-même. Comment styliser la structure interne d'un composant? Des propriétés CSS personnalisées sont utilisées à cet effet.
▍Création de crochets de style à l'aide de propriétés CSS personnalisées
Les utilisateurs peuvent personnaliser les styles des structures internes des composants si l'auteur du composant leur fournit des crochets de style à l'aide de
propriétés CSS personnalisées .
Cette approche est basée sur un mécanisme similaire à celui utilisé lors de l'utilisation des balises
<slot>
, mais elle s'applique, dans ce cas, aux styles.
Prenons un exemple:
<style> custom-container { margin-bottom: 60px; - custom-container-bg: black; } </style> <custom-container background>…</custom-container>
Voici ce qu'il y a dans l'arborescence DOM fantôme:
:host([background]) { background: var( - custom-container-bg, #CECECE); border-radius: 10px; padding: 10px; }
Dans ce cas, le composant utilise le noir comme couleur d'arrière-plan, car c'est l'utilisateur qui l'a spécifié. Sinon, la couleur d'arrière-plan sera
#CECECE
.
En tant qu'auteur du composant, vous êtes responsable d'indiquer à ses utilisateurs les propriétés CSS spécifiques qu'ils peuvent utiliser. Considérez cette partie de l'interface ouverte de votre composant.
API JavaScript pour travailler avec les slots
L'API Shadow DOM offre la possibilité de travailler avec des emplacements.
VentEvent slotchange
L'événement
slotchange
lorsque les nœuds placés dans l'emplacement changent. Par exemple, si un utilisateur ajoute ou supprime des nœuds enfants dans le DOM léger:
var slot = this.shadowRoot.querySelector('#some_slot'); slot.addEventListener('slotchange', function(e) { console.log('Light DOM change'); });
Pour suivre d'autres types de modifications dans le DOM léger, vous pouvez utiliser
MutationObserver
dans le constructeur de l'élément. En savoir plus à ce sujet
ici .
▍ Méthode assignée Nœuds ()
La méthode
assignedNodes()
peut être utile si vous avez besoin de savoir quels éléments sont associés à l'emplacement. L'appel de la méthode
slot.assignedNodes()
vous permet de savoir exactement quels éléments sont affichés par le slot. L'utilisation de l'option
{flatten: true}
vous permet d'obtenir le contenu standard de l'emplacement (affiché si aucun noeud n'y était attaché).
Prenons un exemple:
<slot name='slot1'><p>Default content</p></slot>
Imaginez que cet emplacement se trouve dans le composant
<my-container>
.
Jetons un coup d'œil aux différentes utilisations de ce composant et à ce qui sera retourné lors de l'appel de la méthode
assignedNodes()
.
Dans le premier cas, nous ajoutons notre propre contenu à la fente:
<my-container> <span slot="slot1"> container text </span> </my-container>
Dans ce cas, l'appel
assignedNodes()
renverra
[ container text ]
. Notez que cette valeur est un tableau de nœuds.
Dans le second cas, nous ne remplissons pas la fente avec notre propre contenu:
<my-container> </my-container>
L'appel
assignedNodes()
renverra un tableau vide -
[]
.
Si, cependant, vous transmettez le paramètre
{flatten: true}
à cette méthode, l'appel à ce même élément renverra son contenu par défaut:
[ Default content ]
[ Default content ]
[ Default content ]
.
De plus, afin d'accéder à un élément à l'intérieur de l'emplacement, vous pouvez appeler
assignedNodes()
pour vous faire savoir à quel emplacement de composant votre élément est affecté.
Modèle d'événement
Parlons de ce qui se passe lorsqu'un événement qui apparaît dans l'arborescence DOM fantôme apparaît. Le but de l'événement est défini en tenant compte de l'encapsulation prise en charge par la technologie Shadow DOM. Lorsqu'un événement est redirigé, il semble provenir du composant lui-même, et non de son élément interne, qui se trouve dans l'arborescence DOM fantôme et fait partie de ce composant.
Voici une liste des événements qui sont passés à partir de l'arbre d'ombre DOM (ce comportement n'est pas caractéristique de certains événements):
- Événements de mise au point:
blur
, focus
, focus
, focusin
focusout
. - Événements de la souris s:
click
, dblclick
, mousedown
, mouseenter
, mousemove
et autres. - Événements de
wheel
: wheel
. - Événements d'entrée:
beforeinput
input
, input
. - Événements clavier:
keyup
, keyup
. - Événements de
compositionstart
: compositionstart
, compositionupdate
, compositionend
. - Faites glisser les événements:
dragstart
drag
, drag
, drag
, drop
, etc.
Événements personnalisés
Les événements utilisateur par défaut ne quittent pas l'arbre d'ombre DOM. Si vous souhaitez déclencher un événement et que vous souhaitez qu'il quitte le DOM fantôme, vous devez lui fournir les paramètres
bubbles: true
et
composed: true
. Voici à quoi ressemble le défi d'un événement similaire:
var container = this.shadowRoot.querySelector('#container'); container.dispatchEvent(new Event('containerchanged', {bubbles: true, composed: true}));
Prise en charge des navigateurs Shadow DOM
Afin de savoir si le navigateur prend en charge la technologie Shadow DOM, vous pouvez vérifier la présence de
attachShadow
:
const supportsShadowDOMV1 = !!HTMLElement.prototype.attachShadow;
Voici des informations sur la façon dont différents navigateurs prennent en charge cette technologie.
Prise en charge de la technologie Shadow DOM dans les navigateursRésumé
L'arbre DOM fantôme ne se comporte pas comme un arbre DOM normal. En particulier, selon l'auteur de ce document, dans la bibliothèque
SessionStack , cela s'exprime dans la complication de la procédure de suivi des modifications DOM, dont les informations sont nécessaires pour reproduire ce qui s'est passé avec la page. À savoir,
MutationObserver
utilisé pour suivre les modifications. Dans ce cas, l'arborescence shadow DOM ne déclenche pas l'événement
MutationObserver
dans la portée globale, ce qui conduit à la nécessité d'utiliser des approches spéciales pour travailler avec des composants qui utilisent le DOM Shadow.
, - Shadow DOM, , , , .
Chers lecteurs! -, Shadow DOM?
