Capítulo 5 — Formulários HTML¶
Vídeo curto explicativo (link será adicionado posteriormente)
5.1 — O papel dos formulários na Web¶
Vídeo curto explicativo (link será adicionado posteriormente)
Formulários são o principal mecanismo de comunicação bidirecional entre o usuário e um servidor na Web. Enquanto a maioria dos elementos HTML serve para apresentar informações ao usuário — textos, imagens, vídeos —, os formulários invertem essa direção: eles coletam dados inseridos pelo usuário e os transmitem para processamento.
Essa distinção é fundamental para compreender o papel dos formulários no desenvolvimento web: o HTML apenas estrutura e envia os dados. Ele não os processa. O processamento — autenticar um login, salvar um cadastro em banco de dados, enviar um e-mail, processar um pagamento — é sempre responsabilidade do backend, um programa executado no servidor escrito em linguagens como Python, PHP, Node.js ou Java.
5.1.1 — O fluxo completo de envio de um formulário¶
Quando um usuário preenche e envia um formulário, ocorre a seguinte sequência de eventos:
[1] Usuário preenche os campos e clica em "Enviar"
↓
[2] Navegador coleta os dados dos campos
↓
[3] Navegador monta uma requisição HTTP com os dados
↓
[4] Requisição é enviada ao servidor indicado no atributo action do <form>
↓
[5] Servidor recebe os dados, executa a lógica de negócio e gera uma resposta
↓
[6] Navegador recebe a resposta e exibe o resultado ao usuário
Este fluxo é idêntico ao ciclo de requisição-resposta HTTP estudado no Capítulo 1 — a diferença é que, em vez de simplesmente solicitar um recurso, o navegador está enviando dados junto com a requisição.
Ponto crítico: toda validação realizada no navegador via HTML (atributos
required,pattern, etc.) ou via JavaScript pode ser contornada por um usuário técnico. Por essa razão, a validação no frontend é uma conveniência para o usuário — nunca uma medida de segurança. A validação definitiva sempre deve ocorrer no servidor (backend). Este princípio será aprofundado nos capítulos de back-end e segurança.
5.2 — Estrutura básica: <form>, action e method¶
Vídeo curto explicativo (link será adicionado posteriormente)
O elemento <form> é o contêiner que delimita um formulário HTML. Ele possui dois atributos fundamentais que determinam para onde e como os dados serão enviados:
<form action="/cadastro" method="post">
<!-- campos do formulário -->
</form>
5.2.1 — O atributo action¶
O atributo action especifica a URL de destino para a qual os dados do formulário serão enviados quando o usuário submeter o formulário. Pode ser uma URL absoluta ou relativa:
<!-- URL relativa: envia para /login no mesmo servidor -->
<form action="/login" method="post">...</form>
<!-- URL absoluta: envia para um servidor externo -->
<form action="https://api.exemplo.com/contato" method="post">...</form>
<!-- Sem action: envia para a própria página atual -->
<form method="get">...</form>
Quando action é omitido, o formulário é enviado para a URL da página atual — comportamento útil em páginas que processam o próprio formulário no servidor.
5.2.2 — O atributo method: GET vs POST¶
O atributo method define o método HTTP utilizado para enviar os dados. Os dois valores possíveis são get e post, e a escolha entre eles tem implicações técnicas, semânticas e de segurança significativas.
method="get" — dados na URL
Com o método GET, os dados do formulário são codificados na query string da URL, visíveis na barra de endereços do navegador:
<form action="https://httpbin.org/get" method="get">
<label for="busca">Buscar:</label>
<input type="text" id="busca" name="q" />
<button type="submit">Buscar</button>
</form>
Após o envio com o valor "HTML semântico", a URL resultante seria:
https://httpbin.org/get?q=HTML+sem%C3%A2ntico
Os dados aparecem após o ? na forma de pares nome=valor, separados por & quando há múltiplos campos.
method="post" — dados no corpo da requisição
Com o método POST, os dados são enviados no corpo (body) da requisição HTTP, invisíveis na URL:
<form action="https://httpbin.org/post" method="post">
<label for="email">E-mail:</label>
<input type="email" id="email" name="email" />
<label for="senha">Senha:</label>
<input type="password" id="senha" name="senha" />
<button type="submit">Entrar</button>
</form>
Após o envio, a URL permanece https://httpbin.org/post — sem nenhum dado exposto.
Comparativo GET vs POST
| Critério | GET | POST |
|---|---|---|
| Dados na URL | ✅ Sim | ❌ Não |
| Histórico do navegador | Dados ficam no histórico | Dados não ficam no histórico |
| Bookmarkável | ✅ Sim | ❌ Não |
| Limite de dados | ~2.000 caracteres (varia por navegador/servidor) | Sem limite prático |
| Cache | Pode ser cacheado | Não é cacheado por padrão |
| Idempotência | Idempotente (repetir não muda estado) | Não idempotente |
| Uso adequado | Buscas, filtros, navegação | Login, cadastro, envio de arquivos, pagamentos |
⚠️ Alerta de segurança: nunca use
method="get"para formulários que transmitem dados sensíveis — senhas, tokens, dados pessoais, informações financeiras. Com GET, esses dados aparecem na URL, ficam armazenados no histórico do navegador e nos logs do servidor, e podem ser expostos no cabeçalho HTTPRefererao navegar para outra página.
5.2.3 — Testando o envio com httpbin.org¶
O serviço httpbin.org é uma ferramenta gratuita que recebe requisições HTTP e retorna seus dados em formato JSON — ideal para inspecionar exatamente o que um formulário está enviando durante o desenvolvimento:
<!-- Teste com GET: observe os dados na URL e no JSON retornado -->
<form action="https://httpbin.org/get" method="get">
<input type="text" name="usuario" placeholder="Seu nome" />
<input type="email" name="email" placeholder="Seu e-mail" />
<button type="submit">Testar GET</button>
</form>
<!-- Teste com POST: observe os dados no corpo JSON retornado -->
<form action="https://httpbin.org/post" method="post">
<input type="text" name="usuario" placeholder="Seu nome" />
<input type="email" name="email" placeholder="Seu e-mail" />
<button type="submit">Testar POST</button>
</form>
Ao submeter esses formulários, o httpbin retorna uma resposta JSON que exibe exatamente quais dados foram recebidos — incluindo cabeçalhos, método, IP de origem e os parâmetros enviados. É uma forma concreta de visualizar a diferença entre GET e POST sem necessidade de um backend próprio.
Acesse: https://httpbin.org
5.3 — Campos de entrada: <input> e seus tipos fundamentais¶
Vídeo curto explicativo (link será adicionado posteriormente)
O elemento <input> é o componente mais versátil dos formulários HTML. Seu comportamento é inteiramente determinado pelo atributo type — que define não apenas a aparência do campo, mas também o teclado virtual exibido em dispositivos móveis, as restrições de entrada aceitas e o comportamento de validação nativa do navegador.
5.3.1 — Os atributos name e id: distinção fundamental¶
Antes de explorar os tipos de <input>, é essencial compreender a diferença entre dois atributos que frequentemente causam confusão em iniciantes: name e id.
name — identidade para envio de dados
O atributo name define o identificador do campo nos dados enviados ao servidor. Quando o formulário é submetido, o navegador monta pares nome=valor para cada campo — e o "nome" é exatamente o valor do atributo name.
⚠️ Erro muito comum: campos sem o atributo
namenão são enviados ao servidor. Um campo pode estar visível, preenchido e validado — mas se não tivername, seus dados simplesmente não chegam ao backend. Este é um dos erros mais frequentes em formulários criados por iniciantes.
<!-- Este campo SERÁ enviado: tem name -->
<input type="text" name="usuario" id="campo-usuario" />
<!-- Este campo NÃO será enviado: não tem name -->
<input type="text" id="campo-teste" />
id — identidade no documento HTML
O atributo id identifica o elemento de forma única no documento HTML. Ele é utilizado para: associar um <label> ao campo (via for), referenciar o campo via CSS (seletores #id) e manipular o campo via JavaScript (document.getElementById). O id não tem nenhuma influência sobre o envio de dados.
Resumo prático:
| Atributo | Para que serve | Obrigatório para envio? |
|---|---|---|
name |
Identificar o dado no servidor | ✅ Sim |
id |
Identificar o elemento no documento | ❌ Não (mas recomendado para acessibilidade) |
Na prática, ambos são geralmente declarados e, por convenção, recebem o mesmo valor — mas são conceitualmente independentes:
<input type="text" name="email" id="email" />
5.3.2 — Tipos fundamentais de <input>¶
type="text" — texto livre
<label for="nome">Nome completo:</label>
<input
type="text"
id="nome"
name="nome"
placeholder="Ex.: Maria Silva"
autocomplete="name"
/>
type="email" — endereço de e-mail
O navegador valida automaticamente o formato básico de e-mail (presença de @ e domínio):
<label for="email">E-mail:</label>
<input
type="email"
id="email"
name="email"
placeholder="usuario@dominio.com"
autocomplete="email"
/>
type="password" — senha
O valor digitado é ocultado visualmente. O dado é enviado em texto simples pelo HTML — a criptografia da transmissão é responsabilidade do protocolo HTTPS, não do formulário:
<label for="senha">Senha:</label>
<input
type="password"
id="senha"
name="senha"
minlength="8"
autocomplete="current-password"
/>
type="number" — valor numérico
<label for="idade">Idade:</label>
<input
type="number"
id="idade"
name="idade"
min="18"
max="120"
step="1"
/>
type="checkbox" — múltiplas escolhas independentes
Checkboxes representam opções independentes entre si — o usuário pode marcar zero, uma ou várias. Cada checkbox envia seu valor apenas quando marcado; quando desmarcado, o campo não é enviado.
<!-- Cada checkbox tem name diferente: são dados independentes -->
<fieldset>
<legend>Tecnologias que você conhece:</legend>
<label>
<input type="checkbox" name="html" value="sim" /> HTML
</label>
<label>
<input type="checkbox" name="css" value="sim" /> CSS
</label>
<label>
<input type="checkbox" name="javascript" value="sim" /> JavaScript
</label>
</fieldset>
Para enviar múltiplos valores sob o mesmo nome (como um array), todos os checkboxes de um grupo recebem o mesmo name com colchetes:
<!-- Todos com o mesmo name: enviados como array no backend -->
<input type="checkbox" name="tecnologias[]" value="html" /> HTML
<input type="checkbox" name="tecnologias[]" value="css" /> CSS
<input type="checkbox" name="tecnologias[]" value="js" /> JavaScript
type="radio" — escolha exclusiva
Radio buttons representam opções mutuamente exclusivas — o usuário deve escolher exatamente uma. O agrupamento é definido pelo atributo name: todos os radio buttons com o mesmo name pertencem ao mesmo grupo, e apenas um pode estar selecionado por vez.
<fieldset>
<legend>Período de preferência:</legend>
<!-- Mesmo name = mesmo grupo = escolha exclusiva -->
<label>
<input type="radio" name="periodo" value="manha" /> Manhã
</label>
<label>
<input type="radio" name="periodo" value="tarde" /> Tarde
</label>
<label>
<input type="radio" name="periodo" value="noite" /> Noite
</label>
</fieldset>
Diferença prática entre checkbox e radio:
checkbox |
radio |
|
|---|---|---|
| Quantas opções o usuário pode escolher | Zero ou mais | Exatamente uma |
| Agrupamento | Por convenção (mesmo name[]) |
Por name (obrigatório) |
| Valor enviado quando não marcado | Nenhum | Nenhum |
| Caso de uso | "Quais linguagens você conhece?" | "Qual seu período preferido?" |
type="submit" — botão de envio
<input type="submit" value="Enviar formulário" />
Na prática, o elemento <button type="submit"> é mais flexível e preferível, pois aceita conteúdo HTML interno (como ícones):
<button type="submit">Enviar formulário</button>
⚠️ Alerta: um elemento
<button>sem o atributotypeexplícito é tratado comotype="submit"por padrão — e submeterá o formulário ao ser clicado, mesmo que o desenvolvedor não tenha essa intenção. Sempre declare otypeexplicitamente em botões dentro de formulários:<button type="submit">Enviar</button> <!-- envia o formulário --> <button type="button">Cancelar</button> <!-- não envia nada --> <button type="reset">Limpar</button> <!-- limpa os campos -->
5.4 — Tipos de input menos conhecidos¶
Vídeo curto explicativo (link será adicionado posteriormente)
Além dos tipos fundamentais, o HTML5 introduziu uma série de tipos de <input> especializados que oferecem interfaces nativas do sistema operacional — seletores de data, sliders, paletas de cor — sem necessidade de JavaScript. A adoção desses tipos melhora significativamente a experiência do usuário, especialmente em dispositivos móveis, onde o navegador exibe teclados virtuais otimizados para cada tipo de dado.
⚠️ Compatibilidade: o suporte a tipos de input avançados varia entre navegadores e sistemas operacionais. Em navegadores que não reconhecem um
typeespecífico, o campo é renderizado comotype="text"— o que garante degradação graceful (o campo ainda funciona, mas sem a interface especializada). Antes de usar esses tipos em produção, verifique o suporte atual em https://caniuse.com.
5.4.1 — Data e hora¶
<!-- Data (ano-mês-dia) -->
<label for="nascimento">Data de nascimento:</label>
<input
type="date"
id="nascimento"
name="nascimento"
min="1900-01-01"
max="2010-12-31"
/>
<!-- Hora (horas:minutos) -->
<label for="horario">Horário preferido:</label>
<input type="time" id="horario" name="horario" min="08:00" max="22:00" />
<!-- Data e hora local (sem fuso horário) -->
<label for="agendamento">Agendamento:</label>
<input type="datetime-local" id="agendamento" name="agendamento" />
<!-- Mês e ano -->
<label for="competencia">Competência:</label>
<input type="month" id="competencia" name="competencia" />
<!-- Semana do ano -->
<label for="semana">Semana de início:</label>
<input type="week" id="semana" name="semana" />
O valor enviado ao servidor segue o formato ISO 8601 (ex.: 2026-03-20 para datas, 14:30 para horas) — independentemente do formato visual exibido ao usuário, que varia conforme as configurações de locale do sistema operacional.
Nota de compatibilidade:
type="date"tem suporte amplo em navegadores modernos, mas a aparência do seletor varia significativamente entre plataformas. Em Safari iOS, o seletor usa rodas giratórias; no Chrome Android, usa um calendário visual. Para projetos que exigem aparência consistente entre plataformas, bibliotecas JavaScript de date picker são frequentemente preferidas.
5.4.2 — Intervalo numérico: type="range"¶
Renderiza um controle deslizante (slider) para seleção de valores numéricos em um intervalo:
<label for="volume">Volume: <span id="valor-volume">50</span>%</label>
<input
type="range"
id="volume"
name="volume"
min="0"
max="100"
step="5"
value="50"
/>
O type="range" não exibe o valor atual por padrão — é necessário JavaScript para atualizar um elemento de texto em tempo real. O valor enviado é numérico.
5.4.3 — Cor: type="color"¶
Abre o seletor de cores nativo do sistema operacional:
<label for="cor-tema">Cor do tema:</label>
<input
type="color"
id="cor-tema"
name="cor_tema"
value="#E8632A"
/>
O valor enviado ao servidor é sempre um código hexadecimal de 6 dígitos em minúsculas (ex.: #e8632a).
Nota de compatibilidade: o seletor de cor nativo varia significativamente entre sistemas operacionais — macOS, Windows e Android exibem interfaces completamente diferentes. O atributo
valuedeve ser sempre um código hexadecimal de 6 dígitos; valores comorgb()ou nomes de cor não são aceitos.
5.4.4 — Upload de arquivo: type="file"¶
Permite que o usuário selecione um ou mais arquivos do dispositivo para envio:
<!-- Arquivo único -->
<label for="curriculo">Currículo (PDF):</label>
<input
type="file"
id="curriculo"
name="curriculo"
accept=".pdf"
/>
<!-- Múltiplos arquivos -->
<label for="fotos">Fotos do projeto:</label>
<input
type="file"
id="fotos"
name="fotos[]"
accept="image/jpeg, image/png, image/webp"
multiple
/>
⚠️ Atenção técnica: formulários com
type="file"obrigatoriamente requeremmethod="post"e o atributoenctype="multipart/form-data"no elemento<form>. Sem essas configurações, o arquivo não é enviado corretamente — apenas o nome do arquivo é transmitido.<form action="/upload" method="post" enctype="multipart/form-data"> <input type="file" name="arquivo" /> <button type="submit">Enviar arquivo</button> </form>
5.4.5 — Outros tipos úteis¶
<!-- Busca: semântica de campo de pesquisa (pode exibir botão de limpar) -->
<input type="search" name="q" placeholder="Buscar..." />
<!-- Telefone: exibe teclado numérico em dispositivos móveis -->
<input type="tel" name="telefone" placeholder="(82) 99999-9999" />
<!-- URL: valida formato de endereço web -->
<input type="url" name="website" placeholder="https://www.exemplo.com" />
<!-- Campo oculto: envia dados sem exibição ao usuário -->
<!-- Uso comum: tokens CSRF, IDs de sessão, dados de contexto -->
<input type="hidden" name="csrf_token" value="abc123xyz" />
5.5 — Elementos de formulário complementares¶
Vídeo curto explicativo (link será adicionado posteriormente)
5.5.1 — <textarea>: texto multilinha¶
O elemento <textarea> cria um campo de texto de múltiplas linhas, adequado para mensagens, descrições e comentários. Diferentemente de <input>, ele possui tag de abertura e fechamento, e seu valor padrão é definido pelo conteúdo entre as tags:
<label for="mensagem">Mensagem:</label>
<textarea
id="mensagem"
name="mensagem"
rows="5"
cols="50"
maxlength="500"
placeholder="Descreva sua dúvida com detalhes..."
></textarea>
Os atributos rows e cols definem o tamanho inicial em linhas e caracteres, respectivamente — mas o usuário pode redimensionar o campo em navegadores que o permitem. O controle preciso do tamanho deve ser feito via CSS (propriedade resize).
5.5.2 — <select>: lista de opções¶
O elemento <select> cria uma lista suspensa (dropdown) de opções. Cada opção é definida por um elemento <option>:
<label for="curso">Curso:</label>
<select id="curso" name="curso">
<!-- O primeiro option como placeholder -->
<option value="">Selecione um curso...</option>
<option value="si">Sistemas de Informação</option>
<option value="cc">Ciência da Computação</option>
<option value="ec">Engenharia da Computação</option>
</select>
Boa prática: sempre inclua um
<option value="">vazio como primeiro item, funcionando como placeholder. Isso evita que a primeira opção real seja selecionada por padrão sem que o usuário tenha feito uma escolha consciente.
O atributo value do <option> define o dado enviado ao servidor — que pode ser diferente do texto exibido ao usuário. Se value for omitido, o texto exibido é enviado como valor.
Agrupando opções com <optgroup>
Para listas longas, <optgroup> organiza as opções em grupos visuais rotulados:
<label for="linguagem">Linguagem de programação:</label>
<select id="linguagem" name="linguagem">
<option value="">Selecione...</option>
<optgroup label="Frontend">
<option value="html">HTML</option>
<option value="css">CSS</option>
<option value="js">JavaScript</option>
</optgroup>
<optgroup label="Backend">
<option value="python">Python</option>
<option value="nodejs">Node.js</option>
<option value="php">PHP</option>
</optgroup>
</select>
Seleção múltipla
O atributo multiple transforma o <select> em uma lista de seleção múltipla. O usuário deve usar Ctrl (ou Cmd) para selecionar mais de uma opção:
<label for="interesses">Áreas de interesse (selecione uma ou mais):</label>
<select id="interesses" name="interesses[]" multiple size="4">
<option value="web">Desenvolvimento Web</option>
<option value="dados">Ciência de Dados</option>
<option value="infra">Infraestrutura e DevOps</option>
<option value="seguranca">Segurança da Informação</option>
</select>
5.5.3 — <datalist>: sugestões automáticas¶
O elemento <datalist> oferece uma lista de sugestões para um campo de texto, sem restringir o usuário a apenas essas opções — ao contrário do <select>, que limita as escolhas disponíveis:
<label for="cidade">Cidade:</label>
<input
type="text"
id="cidade"
name="cidade"
list="sugestoes-cidade"
placeholder="Digite sua cidade..."
/>
<datalist id="sugestoes-cidade">
<option value="Maceió" />
<option value="Arapiraca" />
<option value="Palmeira dos Índios" />
<option value="União dos Palmares" />
<option value="Penedo" />
</datalist>
A associação entre o <input> e o <datalist> é feita pelo atributo list do input, cujo valor deve corresponder ao id do <datalist>.
5.6 — Formulários acessíveis¶
Vídeo curto explicativo (link será adicionado posteriormente)
Formulários são componentes de interface que exigem atenção especial à acessibilidade: o usuário precisa compreender o que cada campo solicita, receber feedback quando comete erros e ser capaz de navegar pelo formulário inteiramente via teclado. As fundações da acessibilidade em formulários são alcançadas com elementos e atributos HTML nativos — sem necessidade de ARIA na maioria dos casos.
5.6.1 — <label>: associando rótulos a campos¶
O elemento <label> é o mecanismo fundamental de acessibilidade em formulários. Ele associa um rótulo textual a um campo, tornando claro para o usuário — e para tecnologias assistivas — o que aquele campo solicita.
Existem duas formas de associar um <label> a um campo:
Associação explícita (recomendada): via atributo for no <label> referenciando o id do campo:
<label for="nome">Nome completo:</label>
<input type="text" id="nome" name="nome" />
Associação implícita: envolvendo o campo dentro do <label>:
<label>
Nome completo:
<input type="text" name="nome" />
</label>
Ambas as formas são válidas. A associação explícita é preferida quando o layout visual separa o rótulo do campo.
Por que <label> é indispensável:
- Leitores de tela anunciam o rótulo ao focar o campo — sem ele, o usuário ouve apenas "campo de texto" sem saber o que preencher
- Clicar no
<label>move o foco para o campo associado — ampliando a área clicável, o que beneficia usuários com dificuldades motoras - O WCAG 2.1 exige que todos os campos de formulário tenham um rótulo programaticamente associado (critério 1.3.1, nível A)
⚠️ Erro comum: usar
placeholdercomo substituto de<label>. Oplaceholderdesaparece quando o usuário começa a digitar, o que dificulta a revisão do formulário preenchido.placeholderdeve ser usado apenas como exemplo do formato esperado, nunca como rótulo principal do campo.
5.6.2 — <fieldset> e <legend>: agrupando campos relacionados¶
O elemento <fieldset> agrupa semanticamente campos relacionados de um formulário, e <legend> fornece um título para esse grupo. São especialmente importantes para grupos de radio buttons e checkboxes, onde o <legend> fornece o contexto da pergunta:
<form action="/matricula" method="post">
<fieldset>
<legend>Dados pessoais</legend>
<label for="nome">Nome completo:</label>
<input type="text" id="nome" name="nome" required />
<label for="cpf">CPF:</label>
<input type="text" id="cpf" name="cpf" pattern="\d{3}\.\d{3}\.\d{3}-\d{2}" />
</fieldset>
<fieldset>
<legend>Período de preferência:</legend>
<label>
<input type="radio" name="periodo" value="manha" /> Manhã
</label>
<label>
<input type="radio" name="periodo" value="tarde" /> Tarde
</label>
<label>
<input type="radio" name="periodo" value="noite" /> Noite
</label>
</fieldset>
<button type="submit">Enviar matrícula</button>
</form>
Quando um leitor de tela foca um radio button, ele anuncia: "Manhã, botão de opção, 1 de 3 — Período de preferência". O <legend> fornece o contexto "Período de preferência", sem o qual o usuário ouvia apenas "Manhã" sem saber a qual pergunta a opção pertence.
5.6.3 — ARIA em formulários: uso avançado¶
Os atributos ARIA são utilizados em formulários para situações que os elementos HTML nativos não cobrem diretamente — como mensagens de erro dinâmicas, descrições adicionais e estados de validação. O princípio fundamental é: use elementos HTML nativos primeiro; recorra ao ARIA apenas quando não há elemento semântico adequado.
Os atributos ARIA mais comuns em formulários:
<!-- aria-required: indica campo obrigatório (equivalente semântico ao required) -->
<input type="text" name="nome" aria-required="true" />
<!-- aria-describedby: associa uma descrição adicional ao campo -->
<label for="senha">Senha:</label>
<input
type="password"
id="senha"
name="senha"
aria-describedby="dica-senha"
/>
<p id="dica-senha">A senha deve ter no mínimo 8 caracteres, incluindo letras e números.</p>
<!-- aria-invalid: indica que o campo contém um valor inválido
(normalmente definido via JavaScript após validação) -->
<input
type="email"
name="email"
aria-invalid="true"
aria-describedby="erro-email"
/>
<p id="erro-email" role="alert">Por favor, informe um e-mail válido.</p>
Referência: MDN — Formulários HTML: Validação de dados de formulário
5.7 — Validação nativa do navegador¶
Vídeo curto explicativo (link será adicionado posteriormente)
O HTML5 introduziu um conjunto de atributos que habilitam a validação nativa do navegador — verificação dos dados diretamente no cliente, antes do envio ao servidor. Essa funcionalidade melhora a experiência do usuário ao fornecer feedback imediato, sem necessidade de JavaScript ou de uma viagem ao servidor.
⚠️ Princípio fundamental: a validação nativa do navegador é uma conveniência para o usuário — não é uma medida de segurança. Qualquer usuário com conhecimento técnico pode desabilitar a validação do navegador, modificar o HTML via DevTools ou enviar requisições HTTP diretamente sem usar o formulário. A validação definitiva deve sempre ser implementada no backend. Validações mais complexas no frontend (máscaras, verificação em tempo real, formatação automática) serão abordadas nos capítulos de JavaScript.
5.7.1 — O atributo required¶
Impede o envio do formulário quando o campo está vazio:
<label for="nome">Nome completo: *</label>
<input type="text" id="nome" name="nome" required />
5.7.2 — Restrições de comprimento: minlength e maxlength¶
<label for="bio">Biografia:</label>
<textarea
id="bio"
name="bio"
minlength="50"
maxlength="500"
placeholder="Escreva entre 50 e 500 caracteres..."
></textarea>
5.7.3 — Restrições numéricas: min, max e step¶
<label for="nota">Nota (0 a 10):</label>
<input
type="number"
id="nota"
name="nota"
min="0"
max="10"
step="0.5"
/>
5.7.4 — Validação por padrão: pattern¶
O atributo pattern aceita uma expressão regular que o valor do campo deve satisfazer. É a forma mais poderosa de validação nativa, permitindo definir formatos específicos:
<!-- CEP no formato 00000-000 -->
<label for="cep">CEP:</label>
<input
type="text"
id="cep"
name="cep"
pattern="\d{5}-\d{3}"
placeholder="00000-000"
title="CEP no formato 00000-000"
/>
<!-- Placa de veículo: formato antigo (ABC-1234) ou Mercosul (ABC1D23) -->
<label for="placa">Placa do veículo:</label>
<input
type="text"
id="placa"
name="placa"
pattern="[A-Z]{3}-?\d{4}|[A-Z]{3}\d[A-Z]\d{2}"
title="Placa no formato ABC-1234 ou ABC1D23"
/>
<!-- Senha: mínimo 8 caracteres, ao menos uma letra e um número -->
<label for="nova-senha">Nova senha:</label>
<input
type="password"
id="nova-senha"
name="nova_senha"
pattern="(?=.*[A-Za-z])(?=.*\d).{8,}"
title="Mínimo de 8 caracteres, incluindo ao menos uma letra e um número"
/>
O atributo title fornece a mensagem de erro exibida pelo navegador quando o padrão não é satisfeito — tornando o feedback compreensível para o usuário.
5.7.5 — Validação implícita por type¶
Muitos tipos de <input> realizam validação implícita sem atributos adicionais:
| Tipo | Validação implícita |
|---|---|
email |
Verifica presença de @ e formato básico |
url |
Verifica formato de URL (protocolo + domínio) |
number |
Aceita apenas valores numéricos |
date |
Aceita apenas datas válidas no formato do sistema |
tel |
Sem validação de formato (formatos variam por país) |
5.7.6 — Desabilitando a validação nativa¶
Em alguns cenários — como quando se implementa validação customizada via JavaScript — pode ser necessário desabilitar a validação nativa com o atributo booleano novalidate no elemento <form>:
<form action="/cadastro" method="post" novalidate>
<!-- Validação gerenciada inteiramente por JavaScript -->
</form>
5.8 — Formulário completo: exemplo integrado¶
A seguir, um formulário de contato que integra todos os conceitos abordados neste capítulo — estrutura semântica, acessibilidade, tipos de campos, validação nativa e boas práticas:
<form action="/contato" method="post" novalidate>
<fieldset>
<legend>Dados de identificação</legend>
<div class="campo">
<label for="nome">Nome completo: <span aria-hidden="true">*</span></label>
<input
type="text"
id="nome"
name="nome"
required
minlength="3"
maxlength="100"
autocomplete="name"
aria-required="true"
/>
</div>
<div class="campo">
<label for="email">E-mail: <span aria-hidden="true">*</span></label>
<input
type="email"
id="email"
name="email"
required
autocomplete="email"
aria-required="true"
aria-describedby="dica-email"
/>
<small id="dica-email">Utilizaremos este e-mail apenas para responder sua mensagem.</small>
</div>
<div class="campo">
<label for="telefone">Telefone:</label>
<input
type="tel"
id="telefone"
name="telefone"
pattern="\(\d{2}\)\s\d{4,5}-\d{4}"
placeholder="(82) 99999-9999"
title="Telefone no formato (DDD) NNNNN-NNNN"
autocomplete="tel"
/>
</div>
</fieldset>
<fieldset>
<legend>Assunto da mensagem</legend>
<div class="campo">
<label for="tipo">Tipo de solicitação: <span aria-hidden="true">*</span></label>
<select id="tipo" name="tipo" required aria-required="true">
<option value="">Selecione...</option>
<option value="duvida">Dúvida sobre o curso</option>
<option value="sugestao">Sugestão</option>
<option value="problema">Relato de problema</option>
<option value="outro">Outro assunto</option>
</select>
</div>
<div class="campo">
<label for="mensagem">Mensagem: <span aria-hidden="true">*</span></label>
<textarea
id="mensagem"
name="mensagem"
required
minlength="20"
maxlength="1000"
rows="6"
aria-required="true"
placeholder="Descreva sua solicitação com detalhes..."
></textarea>
</div>
</fieldset>
<fieldset>
<legend>Como prefere ser contatado?</legend>
<label>
<input type="radio" name="contato" value="email" checked />
Por e-mail
</label>
<label>
<input type="radio" name="contato" value="telefone" />
Por telefone
</label>
</fieldset>
<div class="campo">
<label>
<input type="checkbox" name="aceite" value="sim" required />
Li e aceito os <a href="/termos">termos de uso</a>
</label>
</div>
<!-- Campo oculto: dado de contexto enviado automaticamente -->
<input type="hidden" name="origem" value="pagina-contato" />
<div class="acoes">
<button type="reset">Limpar formulário</button>
<button type="submit">Enviar mensagem</button>
</div>
</form>
5.9 — Boas práticas em formulários HTML¶
Um formulário tecnicamente correto não é necessariamente um formulário bem projetado. As boas práticas a seguir reúnem os princípios de usabilidade, acessibilidade e integridade de dados mais relevantes para o desenvolvimento de formulários na Web:
Sempre declare name em campos que devem ser enviados. Campos sem name são silenciosamente ignorados pelo navegador — um dos erros mais difíceis de diagnosticar em formulários.
Sempre use <label> associado a cada campo. placeholder não é um substituto de <label> — ele desaparece quando o usuário começa a digitar e não é anunciado de forma consistente por leitores de tela.
Declare type explicitamente em todos os <button>. O padrão type="submit" de um <button> sem type causa envios acidentais de formulário com frequência.
Agrupe campos relacionados com <fieldset> e <legend>. Isso é especialmente importante para grupos de radio buttons e checkboxes, onde o <legend> fornece o contexto da pergunta.
Use o método HTTP correto. GET para buscas e filtros (os parâmetros na URL são desejáveis); POST para ações que modificam estado no servidor (cadastros, logins, envios de mensagem). Nunca use GET para dados sensíveis.
Informe o usuário sobre campos obrigatórios. A convenção * é amplamente reconhecida, mas deve ser acompanhada de uma nota como "* campos obrigatórios" no início ou no final do formulário.
Forneça mensagens de erro claras. O atributo title em conjunto com pattern melhora as mensagens de erro nativas do navegador. Mensagens genéricas como "Valor inválido" são inadequadas — informe o formato esperado.
Nunca confie apenas na validação do frontend. Toda validação HTML e JavaScript pode ser contornada. A validação definitiva ocorre no servidor.
Prefira autocomplete adequado. O atributo autocomplete com valores semânticos corretos (name, email, tel, current-password, etc.) melhora a experiência do usuário e é reconhecido por gerenciadores de senha.
Referências: - MDN — Guia de formulários HTML - WebAIM — Creating Accessible Forms - W3C — Web Accessibility Tutorials: Forms - WHATWG — The form element
Atividades — Capítulo 5¶
1. Um formulário tem um campo <input type="text" id="usuario"> sem o atributo name. O que ocorre quando o formulário é enviado?
2. Qual é a diferença fundamental entre type="checkbox" e type="radio"?
3. Por que a validação nativa do navegador (atributos required, pattern, etc.) não substitui a validação no servidor?
4. Um <button> sem o atributo type está dentro de um <form>. O que acontece quando ele é clicado?
- GitHub Classroom: Construir um formulário de matrícula com:
<fieldset>e<legend>para agrupamento, ao menos um campo de cada tipo (text,email,select,radio,checkbox,textarea), todos comname,ide<label>associado, validação nativa comrequiredepattern, e envio parahttps://httpbin.org/postpara inspeção da requisição. (link será adicionado)
:material-arrow-left: Voltar ao Capítulo 4 — Tabelas, Listas e Mídia :material-arrow-right: Ir ao Capítulo 6 — Introdução à Acessibilidade Web