# Aula 1.4 - JavaScript no navegador

## Identificação

- **Duração:** 2 horas
- **Tipo:** teoria aplicada e laboratório
- **Entrega:** formulário interativo com validação e retorno acessível
- **Página de referência:** [`../exemplos/aula-1.2/index.html`](../exemplos/aula-1.2/index.html)
- **JavaScript de referência:** [`../exemplos/aula-1.2/assets/main.js`](../exemplos/aula-1.2/assets/main.js)

## Introdução

### O que é JavaScript?

JavaScript é uma linguagem de programação criada para adicionar comportamento às páginas Web. Com ela, a aplicação pode reagir a ações, validar formulários, atualizar conteúdos, fazer requisições e controlar estados.

Sem JavaScript, a página ainda pode possuir conteúdo, links e formulários. Com JavaScript, ela passa a responder de maneira mais dinâmica.

### JavaScript é a mesma coisa que Java?

Não. São linguagens diferentes, com histórias, ambientes e características diferentes. A semelhança no nome não significa que uma seja versão da outra.

### Onde o JavaScript funciona?

Todo navegador moderno possui um mecanismo capaz de executar JavaScript. O código pode acessar a representação da página, chamada DOM, e utilizar recursos oferecidos pelo navegador.

JavaScript também pode funcionar fora do navegador. Node.js, por exemplo, permite executar JavaScript no servidor. Nesta aula trabalharemos com o ambiente do navegador.

```text
JavaScript no navegador -> interface e interação
JavaScript no servidor -> APIs, regras e integrações
```

### Preciso de uma biblioteca?

Não. O navegador já oferece DOM, eventos, formulários, Fetch e várias APIs. Frameworks serão introduzidos depois que os fundamentos da linguagem e do ambiente estiverem claros.

## Objetivos

Ao final da aula, o aluno deverá conseguir:

1. explicar o papel do JavaScript no navegador;
2. carregar um arquivo como módulo;
3. selecionar elementos do DOM;
4. reagir a eventos;
5. ler dados de um formulário;
6. utilizar validação nativa;
7. controlar estados de carregamento, sucesso e erro;
8. atualizar a interface sem recarregar a página;
9. preservar acessibilidade ao apresentar retorno;
10. evitar exposição desnecessária de dados pessoais.

## Pré-requisitos

- HTML semântico;
- formulário com rótulos associados;
- CSS responsivo;
- noções iniciais de variáveis, funções e objetos.

## Pergunta orientadora

> Como transformar uma página visual em uma interface que reage às ações do usuário de forma previsível e acessível?

## Roteiro sugerido

| Etapa | Duração |
|---|---:|
| JavaScript e o navegador | 15 min |
| DOM e seleção de elementos | 20 min |
| Eventos | 20 min |
| Formulários e validação | 25 min |
| Estados da interface | 15 min |
| Laboratório e testes | 25 min |

## 1. Responsabilidade do JavaScript

Na aplicação:

```text
HTML -> estrutura e significado
CSS -> apresentação
JavaScript -> comportamento
```

JavaScript pode:

- reagir a cliques e digitação;
- validar dados;
- atualizar elementos;
- fazer requisições;
- controlar estados;
- armazenar pequenas preferências;
- integrar recursos do navegador.

JavaScript não deve corrigir uma estrutura HTML inadequada. Um formulário continua precisando de `form`, `label`, `input` e `button`.

## 2. Carregando o arquivo

Antes do fechamento de `body`:

```html
<script type="module" src="assets/main.js"></script>
```

`type="module"` permite:

- utilizar `import` e `export`;
- evitar variáveis globais acidentais;
- carregar o script de maneira deferida;
- organizar o código em arquivos.

## 3. DOM

DOM significa Document Object Model. O navegador representa o HTML como objetos que podem ser consultados e modificados.

Considere:

```html
<form id="waitlist-form">
  <button type="submit">Quero participar</button>
</form>
```

Seleção:

```js
const form = document.querySelector("#waitlist-form");
const button = form?.querySelector("button[type='submit']");
```

### Seletores úteis

```js
document.querySelector("#identificador");
document.querySelector(".classe");
document.querySelector("[data-state='success']");
document.querySelectorAll(".feature-card");
```

`querySelector` retorna o primeiro elemento correspondente ou `null`.

### Verificação

```js
if (!form || !button) {
  throw new Error("O formulário não foi encontrado.");
}
```

Falhar de maneira explícita durante o desenvolvimento facilita a identificação de mudanças incompatíveis no HTML.

## 4. Leitura e atualização

```js
const title = document.querySelector("h1");

if (title) {
  title.textContent = "Novo título";
}
```

Prefira `textContent` para inserir texto.

Evite inserir conteúdo não confiável com `innerHTML`:

```js
// Perigoso quando message contém entrada externa
status.innerHTML = message;
```

```js
// Insere apenas texto
status.textContent = message;
```

## 5. Eventos

Eventos representam acontecimentos:

- `click`;
- `input`;
- `change`;
- `submit`;
- `focus`;
- `blur`;
- `keydown`.

Exemplo:

```js
button.addEventListener("click", () => {
  console.log("Botão ativado");
});
```

Para formulários, escute `submit`, não apenas o clique do botão. Assim o fluxo também funciona quando o usuário envia com o teclado.

```js
form.addEventListener("submit", (event) => {
  event.preventDefault();
});
```

`preventDefault` impede temporariamente o envio e o recarregamento tradicional.

## 6. FormData

`FormData` coleta campos que possuem `name`:

```js
const formData = new FormData(form);

const name = formData.get("nome");
const email = formData.get("email");
const profile = formData.get("perfil");
```

Isso demonstra a diferença:

- `id` relaciona elementos e permite seleção;
- `name` define a chave utilizada no envio.

### Normalização

```js
function normalizeName(value) {
  return String(value ?? "")
    .trim()
    .replace(/\s+/g, " ");
}
```

Normalizar não significa alterar arbitrariamente os dados. Cada transformação precisa de justificativa.

## 7. Validação

O HTML já contém:

```html
<input type="email" required>
```

O navegador pode verificar:

```js
if (!form.checkValidity()) {
  form.reportValidity();
  return;
}
```

- `checkValidity` retorna o estado;
- `reportValidity` apresenta a mensagem nativa.

### Estado inválido

```js
form.addEventListener(
  "invalid",
  (event) => {
    event.target.setAttribute("aria-invalid", "true");
  },
  true,
);
```

Quando o usuário corrige:

```js
form.addEventListener("input", (event) => {
  event.target.removeAttribute("aria-invalid");
});
```

O terceiro argumento `true` permite observar o evento `invalid` durante a fase de captura.

## 8. Estados da interface

Uma operação assíncrona normalmente possui:

```text
idle -> loading -> success
                  -> error
```

O botão pode comunicar carregamento:

```js
button.disabled = true;
button.textContent = "Enviando...";
form.setAttribute("aria-busy", "true");
```

Ao terminar:

```js
button.disabled = false;
button.textContent = "Quero participar";
form.removeAttribute("aria-busy");
```

O estado precisa ser restaurado mesmo quando ocorrer erro. Para isso, utilizamos `finally`.

## 9. Operações assíncronas

Uma requisição demora algum tempo.

```js
async function submitForm(payload) {
  const response = await fetch("/api/waitlist", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(payload),
  });

  if (!response.ok) {
    throw new Error("Não foi possível concluir o cadastro.");
  }

  return response.json();
}
```

Nesta aula, a API ainda não existe. A página utiliza uma função simulada. O contrato, porém, prepara a integração futura.

## 10. Retorno acessível

No HTML:

```html
<p
  id="form-status"
  class="form-status"
  role="status"
  aria-live="polite"
  hidden
></p>
```

No JavaScript:

```js
function setStatus(message, state) {
  status.textContent = message;
  status.dataset.state = state;
  status.hidden = false;
}
```

`aria-live="polite"` permite que tecnologias assistivas anunciem a atualização sem interromper imediatamente o usuário.

## 11. Privacidade

O formulário contém nome e e-mail. Durante o desenvolvimento:

- não registre os dados no console;
- não coloque dados pessoais em URLs;
- não armazene e-mail em `localStorage`;
- não envie dados para serviços não autorizados;
- não mantenha dados além da necessidade;
- utilize valores fictícios nos testes.

Este código simula o envio e descarta os dados.

## 12. Implementação completa

Elementos:

```js
const form = document.querySelector("#waitlist-form");
const submitButton = form?.querySelector("button[type='submit']");
const status = document.querySelector("#form-status");
```

Fluxo:

```js
form.addEventListener("submit", async (event) => {
  event.preventDefault();

  if (!form.checkValidity()) {
    form.reportValidity();
    return;
  }

  const formData = new FormData(form);
  const payload = {
    nome: normalizeName(formData.get("nome")),
    email: String(formData.get("email") ?? "").trim().toLowerCase(),
    perfil: String(formData.get("perfil") ?? ""),
    aceite: formData.get("aceite") === "on",
  };

  setLoading(true);

  try {
    await simulateRequest(payload);
    form.reset();
    setStatus("Cadastro simulado com sucesso.", "success");
  } catch {
    setStatus("Não foi possível concluir. Tente novamente.", "error");
  } finally {
    setLoading(false);
  }
});
```

Não utilizamos o objeto `payload` para escrever dados na tela ou no console.

## 13. Laboratório guiado

### Passo 1 - Identificadores

Adicione ao formulário:

```html
id="waitlist-form"
```

Adicione o elemento de retorno.

### Passo 2 - Script

Crie `assets/main.js` e conecte-o como módulo.

### Passo 3 - Seleção segura

Selecione formulário, botão e status. Interrompa a execução com erro claro caso estejam ausentes.

### Passo 4 - Validação

Observe os eventos `invalid` e `input`. Aplique e remova `aria-invalid`.

### Passo 5 - Envio

Escute `submit`, gere `FormData` e normalize os valores.

### Passo 6 - Loading

Durante a operação:

- desabilite o botão;
- altere seu texto;
- aplique `aria-busy`.

### Passo 7 - Resultado

Apresente sucesso, limpe o formulário e devolva o foco para o status.

O código de referência utiliza `tabindex="-1"` no status para permitir foco programático sem colocá-lo na sequência normal de navegação.

## 14. Testes manuais

### Cenário 1 - Campos vazios

1. Ative o botão sem preencher.
2. Confirme que o navegador bloqueia o envio.
3. Identifique o primeiro campo inválido.

### Cenário 2 - E-mail inválido

1. Preencha o nome.
2. Digite `email-invalido`.
3. Selecione o perfil.
4. Marque o aceite.
5. Tente enviar.

### Cenário 3 - Sucesso

1. Utilize dados fictícios válidos.
2. Envie.
3. Observe o estado de carregamento.
4. Confirme a mensagem.
5. Verifique que o formulário foi limpo.

### Cenário 4 - Teclado

Repita o cenário de sucesso sem utilizar o mouse.

## 15. Exercício de fixação

Adicione um contador ao campo de nome:

```text
0 de 80 caracteres
```

Requisitos:

- atualizar no evento `input`;
- usar `textContent`;
- relacionar o contador ao campo com `aria-describedby`;
- impedir mais de 80 caracteres com `maxlength`;
- não utilizar biblioteca externa.

## 16. Desafio individual

Implemente um modo de falha na simulação:

- quando o e-mail terminar em `@erro.local`, a função deve rejeitar;
- a interface deve apresentar estado de erro;
- os dados não devem ser apagados;
- o botão deve voltar ao estado normal;
- o usuário deve conseguir corrigir e tentar novamente.

Utilize apenas dados fictícios.

## 17. Erros comuns

### Escutar somente o clique

O formulário também pode ser enviado pelo teclado. Escute `submit`.

### Não verificar elementos nulos

Mudanças no HTML podem fazer o seletor deixar de encontrar o elemento.

### Utilizar `innerHTML` com entrada externa

Prefira `textContent`.

### Esquecer de restaurar o botão

Utilize `finally` para remover o estado de carregamento.

### Registrar dados pessoais

Não use `console.log(formData)` em código real.

### Desabilitar validação sem substituí-la

`novalidate` remove a apresentação nativa. Utilize apenas quando a aplicação implementar uma alternativa completa e acessível.

### Limpar o formulário após erro

Em caso de falha, preserve os dados para que o usuário possa corrigir ou reenviar.

### Construir interface apenas com JavaScript

O conteúdo e as ações essenciais devem partir de HTML semântico sempre que possível.

## 18. Lista de verificação de entrega

- [ ] O JavaScript está em arquivo externo.
- [ ] O script utiliza `type="module"`.
- [ ] O formulário possui identificador estável.
- [ ] O código verifica se os elementos existem.
- [ ] O fluxo escuta `submit`.
- [ ] A validação nativa continua ativa.
- [ ] Campos inválidos recebem `aria-invalid`.
- [ ] O estado de loading é visível.
- [ ] O botão fica desabilitado durante a operação.
- [ ] Sucesso e erro possuem mensagens diferentes.
- [ ] O retorno utiliza região viva.
- [ ] O formulário só é limpo após sucesso.
- [ ] Dados pessoais não aparecem no console.
- [ ] O fluxo funciona por teclado.
- [ ] Não existem erros no console.

## 19. Critérios de aceite

A entrega será aceita quando:

1. impedir envio de dados inválidos;
2. aceitar dados fictícios válidos;
3. exibir estado de carregamento;
4. exibir mensagem de sucesso;
5. restaurar o botão após a operação;
6. preservar dados quando ocorrer erro;
7. funcionar com teclado;
8. não registrar nome ou e-mail no console;
9. utilizar `textContent` para retorno;
10. não recarregar a página durante a simulação.

## 20. Perguntas para revisão

1. O que é DOM?
2. Qual é a diferença entre `querySelector` e `querySelectorAll`?
3. Por que devemos escutar `submit`?
4. Para que serve `preventDefault`?
5. Como `FormData` identifica os campos?
6. Qual é a diferença entre `id` e `name`?
7. Para que servem `try`, `catch` e `finally`?
8. Por que devemos evitar `innerHTML` com conteúdo externo?
9. O que `aria-live="polite"` faz?
10. Por que não devemos registrar dados pessoais no console?

## Próxima aula

Na Aula 1.5, utilizaremos DevTools e auditorias para analisar acessibilidade, rede, desempenho, erros e riscos básicos de segurança da página.
