# Introdução aos Web Components

> Este é o primeiro artigo de uma série onde iremos entender os conceitos básicos sobre Web Components e como utilizá-los em qualquer projeto (independente do framework) 🙂

Os web components permitem a criação de elementos HTML customizados, reutilizáveis e com suas funcionalidades encapsuladas. Para isso são utilizadas, basicamente, três tecnologias:

1. [Custom Elements](https://developer.mozilla.org/pt-BR/docs/Web/Web_Components/Using_custom_elements)
2. [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM)
3. [HTML Templates](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_templates_and_slots)

O conjunto dessas tecnologias é que permite a criação dos *Web Components*, no entanto, cada uma delas pode ser utilizada de forma isolada. A seguir, vamos entender o funcionamento de cada uma e como utilizá-las na criação dos nossos componentes.

### Custom Elements

Utilizando os **Custom Elements** podemos criar elementos HTML personalizados mantendo toda sua lógica "encapsulada". Simples, certo?


![gatinho_carregando.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1631042571540/In_iEVePW.png)

Por exemplo, imagine que precisamos construir uma lista ordenada onde cada item contém um link que nos encaminha para uma página da [MDN](https://developer.mozilla.org/pt-BR/).

Conhecendo HTML essa é uma tarefa fácil, mas se fosse necessário utilizar essa lista em muitos lugares provavelmente você terminaria com muito código repetido. Nesse caso, podemos criar um elemento customizado e reutilizá-lo em todos os lugares necessários. Vamos criar um elemento desses para nossa lista?

- Primeiramente definimos no nosso JS uma classe responsável por implementar o comportamento do nosso componente e estendemos `HTMLElement`.
- No construtor da classe chamamos o método `super()` para realizar a construção da cadeia de *Prototypes*.
- Por fim, utilizamos a API de *Custom Elements* e registramos nosso elemento, passando como primeiro argumento o nome do nosso elemento (*web-components-list*) e como segundo argumento seu comportamento dado por nossa classe (*WebComponentsList*).

Lembre-se que o nome dos seus componentes deve sempre conter um traço (*kebab-case*) não podendo ser constituído apenas de palavras isoladas.

```jsx
class WebComponentsList extends HTMLElement {
  constructor () {
    super();
  }
}

customElements.define('web-components-list', WebComponentsList);
```

> Mas nossa classe não tem nenhum comportamento além de chamar o método `super()`!

Exatamente, cuidaremos disso agora. Vamos adicionar o HTML necessário para a nossa funcionalidade e atribuí-lo ao `innerHTML` do nosso objeto.

```jsx
class WebComponentsList extends HTMLElement {
  constructor () {
    super();
	this.innerHTML = `
      <ol>
        <li><a href="https://developer.mozilla.org/pt-BR/docs/Web/Web_Components/Using_custom_elements" target="_blank">Custom Elements</a></li>
        <li><a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM" target="_blank">Shadow DOM</a></li>
        <li><a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_templates_and_slots" target="_blank">HTML Templates</a></li>
      </ol>  
    `;
  }
}

customElements.define('web-components-list', WebComponentsList);
```

Agora no nosso HTML podemos simplesmente utilizar nosso componente como se fosse uma *tag* HTML comum. É possível ver o resultado logo abaixo:

%[https://codepen.io/renanmac/pen/RwgaJPv](https://codepen.io/renanmac/pen/RwgaJPv)

Esse foi um caso simples de uso dos *Custom Elements*, no entanto, eles podem ser muito mais poderosos do que isto. Além de serem divididos entre Autônomos e Integrados possuem *callbacks* de ciclo de vida que permitem construir funcionalidades ainda mais complexas. Caso queria entender esses pontos com mais detalhes recomendo essa página ([Usando custom elements](https://developer.mozilla.org/pt-BR/docs/Web/Web_Components/Using_custom_elements)).

## Shadow DOM

Os *Custom Elements* que comentamos anteriormente permitem a criação de elementos personalizados e com seu comportamento encapsulado, certo? **<strike>Errado</strike>** (Mais ou menos na realidade).

A verdade é que mesmo estando "encapsulado" é possível que existam "vazamentos" tanto de dentro do elemento para fora como do elemento para dentro. É aí que entra o **Shadow DOM.**

Ele fornece uma forma de anexarmos, algo como, um "novo" DOM ao DOM Principal. Dessa forma, conseguimos garantir que esses "vazamentos" não ocorram. *([Ainda não sabe o é DOM?](https://developer.mozilla.org/pt-BR/docs/Web/API/Document_Object_Model/Introduction))*


![bebe_yes.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1631042643996/UA0vdRyJL.png)

Vamos retomar o exemplo anterior e criar um Shadow DOM para o nosso elemento 🙂.

- Utilizaremos o método `attachShadow` para criar um Shadow DOM onde adicionaremos nosso componente. O método `attachShadow` recebe como argumento um objeto de opções, no caso, temos apenas a opção `mode` onde seu valor pode ser `open` ou `close`. Utilizar a opção `open` significa que seu Shadow DOM será acessível via JS fora do contexto do seu componente, já com a opção `close` seu Shadow DOM não poderá ser acessado por JS fora do contexto do componente.
- Com a chamada do `attachShadow` criamos nosso Shadow DOM e agora temos acesso ao nó principal desse "novo" DOM a partir da propriedade `shadowRoot`.
- Por fim, adicionaremos nossa lista a propriedade `innerHTML` do nosso `shadowRoot` e seguiremos com a definição do nosso elemento.

```jsx
class WebComponentsList extends HTMLElement {
  constructor () {
    super();
    this.attachShadow({ mode: 'open'});
    this.shadowRoot.innerHTML = `
	  <ol>
        <li><a href="https://developer.mozilla.org/pt-BR/docs/Web/Web_Components/Using_custom_elements" target="_blank">Custom Elements</a></li>
        <li><a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM" target="_blank">Shadow DOM</a></li>
        <li><a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_templates_and_slots" target="_blank">HTML Templates</a></li>
	  </ol>    
	`;
  }
}

customElements.define('web-components-list', WebComponentsList);
```

Podemos ver o resultado logo abaixo 🙂. 

Note que o *CSS* definido fora do nosso componente não exerce nenhum impacto sobre ele, assim como qualquer estilo definido dentro do nosso componente não afetará nenhum elemento de fora. Irado, né?

%[https://codepen.io/renanmac/pen/ExXKrmE](https://codepen.io/renanmac/pen/ExXKrmE)

Deixo [aqui](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM) a documentação da MDN onde você pode conferir outros exemplos, uma explicação de como o Shadow DOM é anexado ao DOM Regular assim como todas as possibilidades de uso.

## HTML Templates

Algo comum de ocorrer no desenvolvimento de aplicações web é ter certas porções de HTML que são muito parecidas entre si. Certo? E se eu dissesse que é possível extrair essas porções e criar templates que podem ser utilizados por toda sua aplicação. Qual seria sua reação?


![leonidas_fala_agora.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1631042705018/Pug-e5pnu.png)

Nossa principal aliada será a tag `<template>` . Com ela podemos definir nosso HTML, assim como os estilos e os scripts, e nada disso será renderizado pelo navegador até que tomemos a decisão de exibi-los.

Retomando nosso exemplo da lista, poderíamos criá-la utilizando um **HTML Template**. O JS responsável pela criação faria o seguinte:

- Buscaria o template através do `querySelector` (ou qualquer outra forma de buscar elementos no DOM)
- Criaria um cópia do **conteúdo** do nosso template através do método `importNode` e o adicionaria ao body.

```jsx
const template = document.querySelector('#web-components-list');
document.body.appendChild(document.importNode(template.content, true));
```

Simples, né? Confere aqui 👇

%[https://codepen.io/renanmac/pen/zYzByvO](https://codepen.io/renanmac/pen/zYzByvO)

Ainda podemos adicionar mais flexibilidade aos nossos templates HTML com o uso dos slots. Mas não vou me estender muito nesse assunto hoje, mas vou deixar o link da documentação, caso queira saber [tudo sobre slots](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_templates_and_slots#adding_flexibility_with_slots).

## Juntando as peças 🧩

Essas três tecnologias combinadas são a base para a criação de **Web Components**. Pensa comigo, agora conseguimos criar um template com nosso HTML, estilos e scripts e deixar tudo organizado em um bloquinho. Podemos adicionar um Shadow DOM e garantir que nada disso vai vazar e impactar outras partes da nossa aplicação, e, para completar, ainda podemos definir um elemento com uma nome bem <strike>sugestivo</strike> bacanudo que vai englobar tudo isso.

Agora ficou fácil de entender, não ficou?


![futurama_agora_eu_entendi](https://cdn.hashnode.com/res/hashnode/image/upload/v1631042741962/D8dPEaOYR.png)

## E agora? 🤔

Falta só criarmos um Web Component para chamar de nosso (❤️), certo? **Certo.**

Mas, para não me alongar demais, vou deixar esse assunto para o próximo post. Enquanto ele não sai porque você não aproveita para comentar o que achou desse post? Você utiliza Web Components? Já utilizou? 

E, se você tiver alguma sugestão de algo que gostaria de ver implementado com Web Components, me conta também 😉.

Forte abraço, até a próxima.

## Referências

1. [https://developer.mozilla.org/pt-BR/docs/Web/Web_Components](https://developer.mozilla.org/pt-BR/docs/Web/Web_Components)
2. [https://css-tricks.com/an-introduction-to-web-components/](https://css-tricks.com/an-introduction-to-web-components/)
