// html · Web Platform Advent #5
Web Components: Custom Elements, Shadow DOM e templates
Crie elementos HTML reutilizáveis com as APIs nativas de Web Components — definir um Custom Element, encapsular estilos com o Shadow DOM, clonar um <template> e passar dados com atributos.
Os Web Components são um conjunto de APIs nativas do navegador que lhe permitem construir os seus próprios elementos HTML reutilizáveis — sem necessidade de framework. Um componente que distribui funciona da mesma forma em HTML puro, React, Vue ou Svelte, porque é apenas um elemento que o navegador compreende. Três APIs tornam isto possível: os Custom Elements, o Shadow DOM e a tag <template>.
Custom Elements
Um Custom Element é uma classe que estende HTMLElement, registada com um nome de tag que tem de conter um hífen (para que nunca possa colidir com uma futura tag integrada):
class GreetingCard extends HTMLElement {
connectedCallback() {
const p = document.createElement('p');
p.textContent = 'Hello from a custom element!';
this.append(p);
}
}
customElements.define('greeting-card', GreetingCard); Uma vez definido, usa-o como qualquer outra tag:
<greeting-card></greeting-card> A classe oferece-lhe callbacks de ciclo de vida. Os mais úteis são connectedCallback (o elemento foi adicionado à página), disconnectedCallback (foi removido) e attributeChangedCallback (um atributo observado mudou).
Reagir a atributos
Para passar dados, leia atributos — exatamente como src numa imagem. Declare quais observar com um getter estático observedAttributes, depois responda em attributeChangedCallback. Construir o nó com textContent mantém os valores fornecidos pelo utilizador em segurança:
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') || 'friend';
this.replaceChildren();
const p = document.createElement('p');
p.textContent = 'Hello, ' + name + '!';
this.append(p);
}
}
customElements.define('greeting-card', GreetingCard); Agora <greeting-card name="Ada"></greeting-card> apresenta "Hello, Ada!", e alterar o atributo ao vivo volta a renderizá-lo.
O Shadow DOM: encapsulamento real
O Shadow DOM dá a um elemento uma subárvore privada cujos estilos e markup estão isolados do resto da página. Um <style> no seu interior nunca pode vazar para fora, e o CSS externo nunca pode alcançá-lo por acidente. Construa-o com a API do DOM e um <slot> para conteúdo projetado:
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); O <slot> é um marcador de posição: o que quer que coloque entre as tags — <fancy-button>Save</fancy-button> — é projetado nesse ponto, de modo que o componente envolve o seu conteúdo sem o substituir.
Templates: clone uma vez, reutilize a baixo custo
O elemento <template> contém markup inerte que é analisado mas não renderizado até que o clone. É a forma eficiente de estampar estruturas repetidas:
<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 = 'Item 1';
document.querySelector('ul').append(node);
</script> As três peças em conjunto
| API | Oferece-lhe |
|---|---|
| Custom Elements | As suas próprias tags com callbacks de ciclo de vida |
| Shadow DOM | Encapsulamento de estilo e markup, slots |
<template> | Markup inerte reutilizável e clonável |
Raramente precisa dos três ao mesmo tempo. Um wrapper simples pode usar apenas um Custom Element; um widget estilizado e autónomo combina um Custom Element com o Shadow DOM e um slot. Como o resultado é DOM puro, integra-se em qualquer stack e sobrevive a qualquer framework que fosse popular no ano em que o escreveu.