Web Components: Crie elementos personalizados com JavaScript
Tags: webcomponents, javascript, html, braziliandevs
Published: 2021-01-13 15:19:17

# O que são (conceito)

Web Components são um conjunto de especificações elaboradas para permitir a criação de elementos web de forma customizada e independente.

Sites construídos com esse conceito tornam-se mais fáceis de manter, isto porque a alteração realizada num elemento será replicada em todo o site, facilitando as alterações e aumentando a produtividade.

A ideia não tão recente, surgiu por volta de 2011 numa conferência de Alex Russell, realizada na [Fronteers Conference](https://fronteers.nl/congres/2011/sessions/web-components-and-model-driven-views-alex-russell). Em 2012, começou a ganhar força com sua especificação de uso publicada no W3.

Apesar das diferenças, a chegada de novas bibliotecas — como a ReactJs e posteriormente a VueJs — ajudou a popularizar essa *nova* forma de programar, que visa separar e encapsular o site em pequenas estruturas.

 > As bibliotecas supramencionadas possuem técnicas e funcionamento diferentes se comparadas ao Web Componentes.

Atualmente, o conjunto de especificações é dividido em 3 peças (ou elementos).

-----------------

# Peças/Elementos

### Peça 1) Shadow DOM

O DOM, na programação/marcação web, é uma API que nos permite acessar ou manipular documentos HTML (e XML). Cada um desses documentos é criado por uma árvore de nós com subnós.

**Documento HTML**

```html
<!DOCTYPE html>
<html>
    <head>
        <title>Olá</title>
    </head>
    <body>
        <span style="color: red">Olá</span>
        <span>Mundo</span>
    </body>
</html>
```

**Árvore de nós do documento supra**

![Árvore de nós do documento](https://dev-to-uploads.s3.amazonaws.com/i/x7m9k1gik6l07krjexwj.png)

O *ShadowDOM* é semelhante ao DOM, a principal diferença está na capacidade que ele tem em definir o encapsulamento de uma árvore DOM, a fim de isolá-la do documento principal. Nos navegadores modernos, podemos ver essa técnica em vários elementos HTML.

O código `<input type="date" />`, por exemplo, gerará uma árvore encapsulada, que  — apesar de um CSS próprio  —  o código não interfere no restante do documento principal e também não é interferido.

![Estrutura escondida do elemento input date](https://dev-to-uploads.s3.amazonaws.com/i/513mytuyoat3dmegd1gk.png)

### Peça 2) Custom Elements

Como o próprio nome diz, esta peça fornece aos desenvolvedores uma maneira de criar elementos HTML personalizados, incluindo visualmente e com eventos específicos.
Adotado pelo Chromium em 2014 em modo experimental, a versão 0 (V0)  — que foi descontinuada em 2016  —  não foi implementada por outros navegadores, que passaram a utilizar as especificações da V1.

**Exemplificação**

![Exemplo utilizando o componente emoji-picker](https://dev-to-uploads.s3.amazonaws.com/i/q6hq3x5uf78xg6vmthuh.png)

### Peça 3) Template

Esta é usada como um fragmento de documento (*DocumentFragment*). Apesar de ele ser adicionado no documento HTML, o navegador ignora-o durante a renderização. Via *JavaScript*, é possível clonar e/ou modificá-lo antes de o inserir na árvore DOM.

**Slots**

Elementos *slot* são usados dentro do elemento *template* para indicar onde determinado(s) conteúdo(s) será(ão) renderizado(s).

# Vantagens e Desvantagem

**Vantagens**

 - **Reutilização:** Podemos utilizar o mesmo elemento em diversos lugares do projeto, de forma simples.
 - **Produtividade:** A alteração do visual, forma ou adição de novos elemento é facilitada.
 - **Encapsulamento:** Com um elemento encapsulado, é possível trabalhar com mais liberdade e evitando conflitos entre códigos.
 - **Nomenclatura eficiente:** O encapsulamento permite que os atributos como *class* e *id* possam ser objetivos.

**Desvantagens**

 - **Compatibilidade:** É possível usar algumas bibliotecas para tornar os elementos compatíveis com navegadores antigos, mas a falta de suporte nativo é uma desvantagem.
 - **Semântica:** Navegadores e buscadores não entendem o significado do elemento `<emoji-picker />`. Isso pode ser contornado para os buscadores e leitores de tela.
 - **Acessibilidade:** Os elementos nativos possuem uma série de atributos que são utilizados pelos leitores de tela; nos customizados, a sua atenção deverá ser redobrada. É possível melhorar e adaptar essa questão.
 - **JavaScript:** É necessário que o JavaScript esteja habilitado para o componente funcionar. Navegadores com o recurso desabilitado ou de linha de comando ([Lynx](https://invisible-island.net/lynx/), por exemplo) poderão não funcionar corretamente (mas nem o Xavier Vídeos funciona).

-----------

# Criando o primeiro componente

No primeiro exemplo, será criado um componente bem simples, cujo objetivo é dar boas vindas ao usuário.

### Base

{% gist https://gist.github.com/valdeirpsr/77a122aa6f26efb5642966754652a134 %}

Na linha 1, foi definido uma classe que estende os atributos e métodos da classe *HTMLElement*. Todo componente **deve** ser filho da *HTMLElement*, caso contrário o navegador não conseguirá executar e invocar os métodos necessários.

Na linha 2 e 3; foi, respectivamente, definida a função construtora e a invocação da função *constructor* da classe pai, que é obrigatória. A utilização da função `constructor` é opcional. Ela é chamada durante a criação ou atualização do componente e pode ser utilizada para criação do *ShadowRoot*, criação de eventos. No entanto, ao usá-la, é necessário saber que há algumas restrições, são elas:

 - A função `super` **deve** ser invocada logo após a criação do construtor;
 - É proibido retornar um valor, exceto `return` (*void*) ou `return this`;
 - A utilização das funções `document.write` e `document.open` é proibida;
 - Os atributos e filhos do elemento não devem ser inspecionados;
O elemento não deve ganhar nenhum atributo ou filho. Essas alterações violam o método `createElement` ao ser usado para criação;
 - Evitem trabalhar com renderização no construtor, optem pelo método `connectedCallback`;

Na linha 5, criamos o *ShadowRoot*. Nele, vamos adicionar os elementos necessário para a renderização. Há dois modos possíveis:

 - *open:* Permite que o código exterior tenha acesso aos elementos do componente usando o atributo *shadowRoot*, por exemplo:

```javascript
document
 .querySelector('v-welcome')
 .shadowRoot
 .querySelector('div')
```

 - *closed:* Não permite que o código exterior tenha acesso aos elementos filhos do componente.

Na linha 8, foi definida a função `attributesChangedCallback`, que será invocada sempre que houver uma adição, atualização, remoção ou substituição de atributos. Para receber tais atualizações, é necessário reescrever o método estático `observedAttributes` e retornar um *array* de *strings* com os atributos que serão "ouvidos" (linhas 30, 31 e 32) do código *Código 01 - Base*.

Na linha 16, temos o método `connectedCallback`, que será invocado quando o componente for adicionado ao DOM. Este método é recomendado para execução de códigos de instalação e renderização.

O método `disconnectedCallback`, linha 26, é executado no momento que o elemento for removido do DOM. É ideal para remoção de eventos e limpeza do componente.

Na linha 35, foi definido o nome do componente e a classe responsável por ele. É importante o nome seguir a regra *"caracteres-nome_do_componente"*, dessa forma o navegador identificará que é um componente customizado.

### Utilizando componente

Neste tópico, serão abordadas duas formas de utilização.

**Via HTML**

No HTML, basta carregar o arquivo de *script* (o lugar não é tão importante para o funcionamento, pode ser dentro do `<head>` ou no final do `<body>`). Após carregá-lo, basta utilizar a *tag* com o nome definido no primeiro parâmetro da função `window.customElements.define`

{% gist https://gist.github.com/valdeirpsr/29eac1305f519393d3b387045dc76fd8 %}

**Via JavaScript**

Você também pode usar a função `document.createElement` para criar o elemento e `node.setAttribute('atributo', 'valor')` para definir os valores necessários.

{% gist https://gist.github.com/valdeirpsr/d6c0ac3c5c0ba6debcb16f39b2c35165 %}

**Resultado**

{% codepen https://codepen.io/valdeir2000/pen/eYdjXRZ default-tab=html,result %}

# Exemplo Adicional

Este é um exemplo um pouco mais complexo. Nele, utilizando o elemento `<v-image>` para carregar um *placeholder* enquanto a imagem principal não é carregada. Também adicionamos suporte a alguns filtros e efeitos com CSS.

{% codesandbox kr5rt view=split %}

[GitHub do Projeto](https://github.com/valdeirpsr/example-web-component)

# Conclusão

É isso! Os componentes estão aí para serem explorados e utilizados, obviamente dependendo do projeto e seu público alvo. Eles são uma mão na roda e realmente auxiliam bastante.

Caso tenham alguma dica ou sugestão, é só comentar. Agradeço-os pela leitura.

------------

# Projetos que utilizam Web Components

 - https://lightroom.adobe.com/
 - https://www.youtube.com/
 - https://meet.google.com/
 - https://photos.google.com/
 - https://patternfly.github.io/patternfly-elements/
 - https://github.com/
 - https://www.polymer-project.org/
 - https://en.miui.com/

# Links úteis

 - [Web Components – Hipsters #06](https://hipsters.tech/web-components-hipsters-06/)
 - [Polymer Project](https://www.polymer-project.org/)

# Referências

> MDN (org.). Using custom elements. In: MOZILLA. Using custom elements. [S. l.], [20 - ]. Disponível em: https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements. Acesso em: 3 jul. 2020.
WHATWG (org.). HTML: Scripting. [S. l.], [20 - ]. Disponível em: https://html.spec.whatwg.org/multipage/scripting.html. Acesso em: 8 jul. 2020.
WHATWG (org.). HTML: Custom elements. [S. l.], [20 - ]. Disponível em: https://html.spec.whatwg.org/multipage/custom-elements.html. Acesso em: 12 jul. 2020.
MDN (org.). Web Components: Using shadow DOM. [S. l.], [20 - ]. Disponível em: https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM. Acesso em: 12 jul. 2020.
W3C (org.). Shadow DOM. [S. l.], [20 - ]. Disponível em: https://www.w3.org/TR/shadow-dom/. Acesso em: 3 jul. 2020.
HANASHIRO, Akira. O que é DOM, Virtual DOM e Shadow DOM?. [S. l.], 17 abr. 2020. Disponível em: https://www.treinaweb.com.br/blog/o-que-e-dom-virtual-dom-e-shadow-dom/. Acesso em: 16 jul. 2020.
GLAZKOV, Dimitri. What the Heck is Shadow DOM?. [S. l.], 14 jan. 2011. Disponível em: https://glazkov.com/2011/01/14/what-the-heck-is-shadow-dom/. Acesso em: 16 jul. 2020.
https://github.com/mdn/web-components-examples