Desvendando o AJAX e o
XMLHttpRequest: Um Guia Didático
Introdução: O que é AJAX?
AJAX, acrônimo para Asynchronous JavaScript and XML, é uma técnica de
desenvolvimento web que permite que aplicações web funcionem de forma
assíncrona, ou seja, processem solicitações ao servidor em segundo plano, sem a
necessidade de recarregar a página inteira. Isso resulta em uma experiência de usuário
muito mais fluida e dinâmica, semelhante à de um aplicativo de desktop.
Apesar de ter "XML" em seu nome, o AJAX não se limita a esse formato de dados.
Atualmente, é muito mais comum o uso de JSON (JavaScript Object Notation) para a
troca de informações entre o cliente e o servidor, por ser mais leve e fácil de manipular
com JavaScript.
O Coração do AJAX: XMLHttpRequest (XHR)
O objeto XMLHttpRequest (XHR) é a peça fundamental que torna o AJAX possível. Ele
é uma API disponível nos navegadores que permite que o código JavaScript faça
requisições HTTP para um servidor web. Com o XHR, é possível enviar e receber dados
em diversos formatos, como texto, XML, JSON, e até mesmo arquivos binários.
O XHR opera de forma assíncrona, o que significa que, uma vez que uma requisição é
enviada, o código JavaScript não precisa esperar pela resposta do servidor para
continuar sua execução. Em vez disso, ele pode registrar uma função de callback que
será executada quando a resposta estiver disponível.
Analisando o Formulário de Entrega
O código HTML que você forneceu cria um formulário de entrega simples. Vamos
analisar cada parte dele:
Estrutura Básica do HTML
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="utf-8" />
<title>Formulário de Entrega (sem libs)</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="./[Link]" />
<script defer src="./[Link]"></script>
</head>
<body>
...
</body>
</html>
<!DOCTYPE html> : Define o tipo de documento como HTML5.
<html lang="pt-BR"> : O elemento raiz da página, com o atributo lang
indicando que o idioma principal é o português do Brasil.
<head> : Contém metadados sobre o documento, como o título, a codificação de
caracteres e links para folhas de estilo e scripts.
<meta charset="utf-8" /> : Especifica a codificação de caracteres como UTF-8,
que suporta a maioria dos caracteres especiais.
<title> : Define o título da página, que aparece na aba do navegador.
<meta name="viewport" ...> : Garante que a página seja exibida corretamente
em dispositivos móveis, ajustando a largura ao tamanho da tela.
<link rel="stylesheet" href="./[Link]" /> : Vincula uma folha de
estilos externa para estilizar a página.
<script defer src="./[Link]"></script> : Vincula um arquivo JavaScript
externo. O atributo defer garante que o script seja executado somente após a
análise completa do HTML.
<body> : Contém o conteúdo visível da página.
Conteúdo Principal e Formulário
<main class="container">
<h1>Dados para Entrega</h1>
<form id="form-pedido" autocomplete="on" novalidate>
...
</form>
</main>
<main class="container"> : O conteúdo principal da página, agrupado em um
contêiner para estilização.
<h1> : O título principal da página.
<form id="form-pedido" ...> : O formulário em si.
id="form-pedido" : Um identificador único para o formulário, que será
usado pelo JavaScript.
autocomplete="on" : Permite que o navegador preencha automaticamente
os campos com base em dados inseridos anteriormente.
novalidate : Desativa a validação de formulário nativa do navegador,
permitindo que a validação seja feita via JavaScript.
Seções do Formulário
O formulário é dividido em duas seções principais: "Dados básicos" e "Dados da
entrega".
Dados básicos:
<section>
<h2>Dados básicos</h2>
<div class="grid g-3">
<div class="field">
<label for="nome">Nome</label>
<input id="nome" name="nome" type="text" placeholder="Fulano" required />
</div>
...
</div>
</section>
<section> : Agrupa os campos de dados básicos.
<h2> : Um subtítulo para a seção.
<div class="grid g-3"> : Um contêiner para os campos, provavelmente para
aplicar um layout de grade com 3 colunas.
<div class="field"> : Um contêiner para cada campo de formulário (rótulo e
entrada).
<label for="nome"> : O rótulo do campo, associado ao campo de entrada com
o id="nome" .
<input ...> : O campo de entrada de texto para o nome.
id="nome" : Identificador único para o campo.
name="nome" : Nome do campo, que será enviado com os dados do
formulário.
type="text" : Tipo de entrada (texto).
placeholder="Fulano" : Texto de exemplo exibido no campo quando ele
está vazio.
required : Indica que o campo é de preenchimento obrigatório.
Dados da entrega:
<section>
<h2>Dados da entrega</h2>
<div class="grid g-cep">
<div class="field">
<label for="cep">CEP</label>
<div class="input-row">
<input id="cep" name="cep" type="text" inputmode="numeric"
placeholder="Somente números" maxlength="9" />
<button type="button" id="btn-buscar-cep" class="btn">
CEP</button>
🔍 Buscar
</div>
<small class="hint">Dica: digite 01001000 para testar (Praça da Sé, SP).
</small>
</div>
</div>
...
</section>
Esta seção contém os campos para o CEP, endereço e número.
O campo de CEP ( <input id="cep" ...> ) tem alguns atributos interessantes:
inputmode="numeric" : Sugere ao navegador que exiba um teclado
numérico em dispositivos móveis.
maxlength="9" : Limita o número de caracteres que podem ser inseridos
no campo.
O botão "Buscar CEP" ( <button id="btn-buscar-cep" ...> ) é o gatilho para a
nossa requisição AJAX.
O campo de endereço ( <input id="endereco" ...> ) é readonly , o que
significa que o usuário não pode editá-lo diretamente. Ele será preenchido
automaticamente pela nossa requisição AJAX.
Mensagens e Ações
<div id="msg" class="msg" aria-live="polite"></div>
<div class="actions">
<button type="submit" class="btn primary">Enviar pedido</button>
<button type="reset" class="btn secondary">Limpar</button>
</div>
<div id="msg" ...> : Um elemento para exibir mensagens de status para o
usuário (por exemplo, "Buscando CEP...", "CEP não encontrado"). O atributo
aria-live="polite" torna essas mensagens acessíveis a leitores de tela.
<div class="actions"> : Contém os botões de ação do formulário.
<button type="submit" ...> : O botão para enviar o formulário.
<button type="reset" ...> : O botão para limpar todos os campos do
formulário.
O JavaScript por Trás da Mágica: [Link]
Agora, vamos mergulhar no código JavaScript que faz a mágica acontecer. O arquivo
[Link] é responsável por capturar o evento de clique no botão "Buscar CEP", fazer a
requisição AJAX para a API do ViaCEP e atualizar a página com os dados do endereço.
1. Referências dos Elementos Essenciais
const cepInput = [Link]('cep');
const buscarBtn = [Link]('btn-buscar-cep');
const statusEl = [Link]('status') ||
[Link]('msg'); // mensagens
const saidaEl = [Link]('saida');
// JSON bruto (opcional)
const enderecoEl = [Link]('endereco');
// campo de endereço
O primeiro passo é obter referências para os elementos HTML com os quais vamos
interagir. Usamos [Link]() para selecionar cada elemento pelo
seu id e armazená-lo em uma constante para fácil acesso posterior.
2. Funções Auxiliares
function setStatus(txt) {
if (statusEl) [Link] = txt;
else [Link]('[STATUS]', txt);
}
function setSaida(txt) {
if (!saidaEl) return;
[Link] = txt;
}
function setLoading(isLoading) {
if (!buscarBtn) return;
[Link] = isLoading;
[Link] = isLoading ? 'Buscando...' : 'Buscar CEP';
}
Para manter o código organizado e evitar repetição, foram criadas algumas funções
auxiliares:
setStatus(txt) : Atualiza o elemento de mensagem ( <div id="msg"> ) com o
texto fornecido.
setSaida(txt) : Exibe o JSON bruto retornado pela API (útil para depuração).
setLoading(isLoading) : Controla o estado de "carregamento" do botão
"Buscar CEP". Quando isLoading é true , o botão é desativado e seu texto
muda para "Buscando...".
3. A Função Principal: buscarCepXhttp()
Esta é a função onde a requisição AJAX acontece.
function buscarCepXhttp() {
const cep = cepInput ? [Link] : '';
const url = `[Link]
setStatus('Consultando...');
setSaida('');
if (enderecoEl) [Link] = '';
setLoading(true);
// --- AJAX clássico com XMLHttpRequest ---
const xhttp = new XMLHttpRequest();
[Link]('GET', url, true); // método, URL, assíncrono
[Link] = 'text'; // deixamos texto e fazemos [Link]
manual
[Link] = 10000; // boa prática: timeout (10s)
[Link] = function () {
// ... (continua)
};
[Link] = function () {
// ... (continua)
};
[Link] = function () {
// ... (continua)
};
[Link](); // dispara a requisição
}
Vamos quebrar essa função em partes:
1. Preparação:
Obtém o valor do CEP digitado pelo usuário.
Constrói a URL da API do ViaCEP com o CEP fornecido.
Atualiza o status para "Consultando...", limpa a saída de depuração e o
campo de endereço, e ativa o estado de carregamento do botão.
2. Criação e Configuração do XHR:
const xhttp = new XMLHttpRequest(); : Cria uma nova instância do
objeto XMLHttpRequest .
[Link]('GET', url, true); : Configura a requisição.
'GET' : O método HTTP a ser usado (neste caso, para obter dados).
url : A URL para a qual a requisição será enviada.
true : Indica que a requisição deve ser assíncrona.
[Link] = 'text'; : Define o tipo de resposta esperado como
texto. Faremos a conversão para JSON manualmente.
[Link] = 10000; : Define um tempo limite de 10 segundos para a
requisição. Se a resposta não chegar nesse tempo, a requisição será
cancelada.
3. Manipuladores de Eventos:
[Link] : Este é o principal manipulador de eventos.
Ele é chamado sempre que o estado da requisição ( readyState ) muda.
[Link] : É chamado se ocorrer um erro de rede (por exemplo, o
usuário está offline).
[Link] : É chamado se a requisição expirar (atingir o timeout ).
4. Envio da Requisição:
[Link](); : Envia a requisição para o servidor.
4. O Manipulador onreadystatechange
[Link] = function () {
// readyState 4 = resposta finalizada
if ([Link] !== 4) return;
setLoading(false); // chegou resposta (com sucesso ou erro)
if ([Link] === 200) {
// Sucesso HTTP
try {
const data = [Link]([Link]); // converte JSON (texto ->
objeto)
if (data && [Link]) {
setStatus('CEP não encontrado.');
return;
}
setStatus('OK');
const enderecoFmt = `$`{[Link] || ''}, `${[Link] || ''} -
$`{[Link] || ''} - `${[Link] || ''}`.trim();
if (enderecoEl) [Link] = enderecoFmt;
if (saidaEl) setSaida([Link](data, null, 2));
} catch {
setStatus('Resposta inválida do servidor (JSON malformado).');
}
} else {
// Qualquer outro status (400/404/500…)
setStatus(`Falha HTTP $`{[Link]} `${[Link] || ''}`);
}
};
Este é o coração da lógica de tratamento da resposta:
if ([Link] !== 4) return; : O readyState 4 indica que a
requisição foi concluída. Se o estado não for 4, a função simplesmente retorna e
espera pela próxima mudança de estado.
setLoading(false); : Desativa o estado de carregamento do botão, pois a
requisição foi finalizada (com sucesso ou erro).
if ([Link] === 200) : O status 200 significa que a requisição foi bem-
sucedida.
try...catch : Usamos um bloco try...catch para lidar com possíveis
erros ao analisar a resposta JSON.
const data = [Link]([Link]); : Converte a resposta
em texto (JSON) para um objeto JavaScript.
if (data && [Link]) : A API do ViaCEP retorna um objeto com a
propriedade erro se o CEP não for encontrado. Verificamos isso e exibimos
uma mensagem de erro apropriada.
Se o CEP for encontrado, formatamos o endereço e o inserimos no campo
de endereço do formulário.
else : Se o status não for 200, significa que ocorreu um erro no servidor (por
exemplo, 404 - Não Encontrado, 500 - Erro Interno do Servidor). Exibimos uma
mensagem de erro com o código de status HTTP.
5. Eventos de Clique e Tecla
if (buscarBtn) {
[Link]('click', buscarCepXhttp);
}
if (cepInput) {
[Link]('keydown', (e) => {
if ([Link] === 'Enter') {
[Link]();
buscarCepXhttp();
}
});
}
Finalmente, adicionamos os event listeners para acionar a função buscarCepXhttp() :
Um listener de click no botão "Buscar CEP".
Um listener de keydown no campo de CEP, que aciona a busca quando a tecla
"Enter" é pressionada.