// html · Web Platform Advent #5
Web Components : Custom Elements, Shadow DOM et templates
Créez des éléments HTML réutilisables avec les API natives Web Components — définir un Custom Element, encapsuler les styles avec le Shadow DOM, cloner un <template> et passer des données par attributs.
Les Web Components sont un ensemble d'API natives du navigateur qui permettent de créer vos propres éléments HTML réutilisables — sans aucun framework. Un composant que vous publiez fonctionne à l'identique en HTML pur, React, Vue ou Svelte, car c'est simplement un élément que le navigateur comprend. Trois API le rendent possible : les Custom Elements, le Shadow DOM et la balise <template>.
Custom Elements
Un Custom Element est une classe qui étend HTMLElement, enregistrée avec un nom de balise qui doit contenir un trait d'union (pour ne jamais entrer en collision avec une future balise native) :
class GreetingCard extends HTMLElement {
connectedCallback() {
const p = document.createElement('p');
p.textContent = 'Bonjour depuis un custom element !';
this.append(p);
}
}
customElements.define('greeting-card', GreetingCard); Une fois défini, vous l'utilisez comme n'importe quelle balise :
<greeting-card></greeting-card> La classe vous donne des callbacks de cycle de vie. Les plus utiles sont connectedCallback (l'élément a été ajouté à la page), disconnectedCallback (il a été retiré) et attributeChangedCallback (un attribut surveillé a changé).
Réagir aux attributs
Pour passer des données, lisez les attributs — exactement comme src sur une image. Déclarez ceux à surveiller avec un getter statique observedAttributes, puis répondez dans attributeChangedCallback. Construire le nœud avec textContent sécurise les valeurs fournies par l'utilisateur :
class GreetingCard extends HTMLElement {
static get observedAttributes() {
return ['name'];
}
attributeChangedCallback(attr, oldVal, newVal) {
if (attr === 'name') this.render();
}
connectedCallback() {
this.render();
}
render() {
const name = this.getAttribute('name') || 'ami';
this.replaceChildren();
const p = document.createElement('p');
p.textContent = 'Bonjour, ' + name + ' !';
this.append(p);
}
}
customElements.define('greeting-card', GreetingCard); Désormais <greeting-card name="Ada"></greeting-card> affiche « Bonjour, Ada ! », et changer l'attribut en direct le redessine.
Le Shadow DOM : une vraie encapsulation
Le Shadow DOM donne à un élément un sous-arbre privé dont les styles et le balisage sont isolés du reste de la page. Un <style> à l'intérieur ne peut jamais fuir, et le CSS extérieur ne peut jamais y pénétrer par accident. Construisez-le avec l'API DOM et un <slot> pour le contenu projeté :
class FancyButton extends HTMLElement {
connectedCallback() {
const shadow = this.attachShadow({ mode: 'open' });
const style = document.createElement('style');
style.textContent =
'button { background:#2563eb; color:#fff; border:0;' +
' border-radius:8px; padding:.6rem 1rem; }';
const button = document.createElement('button');
button.append(document.createElement('slot'));
shadow.append(style, button);
}
}
customElements.define('fancy-button', FancyButton); Le <slot> est un emplacement réservé : ce que vous placez entre les balises — <fancy-button>Enregistrer</fancy-button> — est projeté à cet endroit, de sorte que le composant enveloppe votre contenu sans le remplacer.
Templates : cloner une fois, réutiliser à bas coût
L'élément <template> contient un balisage inerte, analysé mais non rendu tant que vous ne le clonez pas. C'est la façon efficace de répéter une structure :
<template id="row-tpl">
<li><span class="label"></span></li>
</template>
<script>
const tpl = document.getElementById('row-tpl');
const node = tpl.content.cloneNode(true);
node.querySelector('.label').textContent = 'Élément 1';
document.querySelector('ul').append(node);
</script> Les trois pièces réunies
| API | Ce qu'elle apporte |
|---|---|
| Custom Elements | Vos propres balises avec callbacks de cycle de vie |
| Shadow DOM | Encapsulation du style et du balisage, slots |
<template> | Balisage inerte réutilisable et clonable |
Vous avez rarement besoin des trois à la fois. Un simple conteneur peut n'utiliser qu'un Custom Element ; un widget stylé et autonome combine un Custom Element avec le Shadow DOM et un slot. Comme le résultat est du DOM standard, il s'intègre à n'importe quelle stack et survit au framework à la mode l'année où vous l'avez écrit.