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 - JavaScript de referência:
../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.
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:
- explicar o papel do JavaScript no navegador;
- carregar um arquivo como módulo;
- selecionar elementos do DOM;
- reagir a eventos;
- ler dados de um formulário;
- utilizar validação nativa;
- controlar estados de carregamento, sucesso e erro;
- atualizar a interface sem recarregar a página;
- preservar acessibilidade ao apresentar retorno;
- 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:
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:
<script type="module" src="assets/main.js"></script>
type="module" permite:
- utilizar
importeexport; - 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:
<form id="waitlist-form">
<button type="submit">Quero participar</button>
</form>
Seleção:
const form = document.querySelector("#waitlist-form");
const button = form?.querySelector("button[type='submit']");
Seletores úteis
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
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
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:
// Perigoso quando message contém entrada externa
status.innerHTML = message;
// Insere apenas texto
status.textContent = message;
5. Eventos
Eventos representam acontecimentos:
click;input;change;submit;focus;blur;keydown.
Exemplo:
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.
form.addEventListener("submit", (event) => {
event.preventDefault();
});
preventDefault impede temporariamente o envio e o recarregamento tradicional.
6. FormData
FormData coleta campos que possuem name:
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:
idrelaciona elementos e permite seleção;namedefine a chave utilizada no envio.
Normalização
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:
<input type="email" required>
O navegador pode verificar:
if (!form.checkValidity()) {
form.reportValidity();
return;
}
checkValidityretorna o estado;reportValidityapresenta a mensagem nativa.
Estado inválido
form.addEventListener(
"invalid",
(event) => {
event.target.setAttribute("aria-invalid", "true");
},
true,
);
Quando o usuário corrige:
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:
idle -> loading -> success
-> error
O botão pode comunicar carregamento:
button.disabled = true;
button.textContent = "Enviando...";
form.setAttribute("aria-busy", "true");
Ao terminar:
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.
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:
<p
id="form-status"
class="form-status"
role="status"
aria-live="polite"
hidden
></p>
No JavaScript:
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:
const form = document.querySelector("#waitlist-form");
const submitButton = form?.querySelector("button[type='submit']");
const status = document.querySelector("#form-status");
Fluxo:
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:
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
- Ative o botão sem preencher.
- Confirme que o navegador bloqueia o envio.
- Identifique o primeiro campo inválido.
Cenário 2 - E-mail inválido
- Preencha o nome.
- Digite
email-invalido. - Selecione o perfil.
- Marque o aceite.
- Tente enviar.
Cenário 3 - Sucesso
- Utilize dados fictícios válidos.
- Envie.
- Observe o estado de carregamento.
- Confirme a mensagem.
- 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:
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:
- impedir envio de dados inválidos;
- aceitar dados fictícios válidos;
- exibir estado de carregamento;
- exibir mensagem de sucesso;
- restaurar o botão após a operação;
- preservar dados quando ocorrer erro;
- funcionar com teclado;
- não registrar nome ou e-mail no console;
- utilizar
textContentpara retorno; - não recarregar a página durante a simulação.
20. Perguntas para revisão
- O que é DOM?
- Qual é a diferença entre
querySelectorequerySelectorAll? - Por que devemos escutar
submit? - Para que serve
preventDefault? - Como
FormDataidentifica os campos? - Qual é a diferença entre
idename? - Para que servem
try,catchefinally? - Por que devemos evitar
innerHTMLcom conteúdo externo? - O que
aria-live="polite"faz? - 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.