Aprenda a criar um formulário de endereço completo com dropdowns país → estado → cidade usando os endpoints select da CountryDataAPI.
Este guia mostra como criar inputs select em cascata para:
Os endpoints select são otimizados para formulários, retornando apenas os campos id e name para tamanho mínimo de payload e máximo desempenho.
Primeiro, carregue todos os países quando sua página carregar. Os países raramente mudam, então você pode armazenar esses dados em cache no localStorage para melhor desempenho.
const API_KEY = 'sua-chave-api';
const BASE_URL = 'https://api.countrydataapi.com/v1';
async function loadCountries() {
try {
const response = await fetch(
`${BASE_URL}/select/countries?apikey=${API_KEY}&lang=pt`
);
const { success, data, error } = await response.json();
if (!success) {
console.error('Erro da API:', error);
return;
}
const countrySelect = document.getElementById('country');
data.forEach(country => {
const option = new Option(country.name, country.id);
countrySelect.add(option);
});
} catch (err) {
console.error('Erro de Rede:', err);
}
}
// Chamar ao carregar a página
document.addEventListener('DOMContentLoaded', loadCountries);
Dica: Armazene a lista de países em cache no localStorage para evitar chamadas repetidas à API. Os países não mudam frequentemente!
Quando um usuário selecionar um país, carregue os estados/províncias correspondentes e redefina o dropdown de cidades.
document.getElementById('country').addEventListener('change', async (e) => {
const countryId = e.target.value;
const stateSelect = document.getElementById('state');
const citySelect = document.getElementById('city');
// Redefinir dropdowns de estado e cidade
stateSelect.innerHTML = '<option value="">Selecione o estado...</option>';
citySelect.innerHTML = '<option value="">Selecione a cidade...</option>';
stateSelect.disabled = true;
citySelect.disabled = true;
if (!countryId) return;
try {
const response = await fetch(
`${BASE_URL}/select/states?apikey=${API_KEY}&country=${countryId}&lang=pt`
);
const { success, data, error } = await response.json();
if (!success) {
console.error('Erro da API:', error);
return;
}
data.forEach(state => {
stateSelect.add(new Option(state.name, state.id));
});
stateSelect.disabled = false;
} catch (err) {
console.error('Erro de Rede:', err);
}
});
Por fim, carregue as cidades quando um estado for selecionado.
document.getElementById('state').addEventListener('change', async (e) => {
const stateId = e.target.value;
const citySelect = document.getElementById('city');
// Redefinir dropdown de cidade
citySelect.innerHTML = '<option value="">Selecione a cidade...</option>';
citySelect.disabled = true;
if (!stateId) return;
try {
const response = await fetch(
`${BASE_URL}/select/cities?apikey=${API_KEY}&state=${stateId}&lang=pt`
);
const { success, data, error } = await response.json();
if (!success) {
console.error('Erro da API:', error);
return;
}
data.forEach(city => {
citySelect.add(new Option(city.name, city.id));
});
citySelect.disabled = false;
} catch (err) {
console.error('Erro de Rede:', err);
}
});
<!DOCTYPE html>
<html lang="pt">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Formulário de Endereço - CountryDataAPI</title>
<style>
body {
font-family: system-ui, -apple-system, sans-serif;
max-width: 600px;
margin: 50px auto;
padding: 20px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #333;
}
select {
width: 100%;
padding: 10px;
border: 2px solid #e2e8f0;
border-radius: 6px;
font-size: 16px;
background-color: white;
cursor: pointer;
}
select:disabled {
background-color: #f7fafc;
cursor: not-allowed;
opacity: 0.6;
}
select:focus {
outline: none;
border-color: #3b82f6;
}
button {
background-color: #3b82f6;
color: white;
padding: 12px 24px;
border: none;
border-radius: 6px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
width: 100%;
}
button:disabled {
background-color: #cbd5e0;
cursor: not-allowed;
}
</style>
</head>
<body>
<h1>Formulário de Endereço</h1>
<form id="address-form">
<div class="form-group">
<label for="country">País *</label>
<select id="country" name="country" required>
<option value="">Selecione o país...</option>
</select>
</div>
<div class="form-group">
<label for="state">Estado *</label>
<select id="state" name="state" required disabled>
<option value="">Selecione o estado...</option>
</select>
</div>
<div class="form-group">
<label for="city">Cidade *</label>
<select id="city" name="city" required disabled>
<option value="">Selecione a cidade...</option>
</select>
</div>
<div class="form-group">
<label for="street">Endereço *</label>
<input type="text" id="street" name="street" required
style="width: 100%; padding: 10px; border: 2px solid #e2e8f0; border-radius: 6px;">
</div>
<button type="submit">Enviar Endereço</button>
</form>
<script src="address-form.js"></script>
</body>
</html>
Trate o envio do formulário para obter os valores selecionados:
document.getElementById('address-form').addEventListener('submit', (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const address = {
country: formData.get('country'),
state: formData.get('state'),
city: formData.get('city'),
street: formData.get('street')
};
console.log('Endereço enviado:', address);
// Envie para seu backend ou processe conforme necessário
// fetch('/api/save-address', {
// method: 'POST',
// headers: { 'Content-Type': 'application/json' },
// body: JSON.stringify(address)
// });
});
Esta implementação é altamente eficiente no uso de tokens:
Exemplo: Um usuário preenchendo o formulário completamente usa apenas 3 tokens no total.
Os países não mudam frequentemente. Armazene-os em cache no localStorage:
async function loadCountries() {
const cached = localStorage.getItem('countries');
if (cached) {
const data = JSON.parse(cached);
populateCountrySelect(data);
return;
}
const response = await fetch(
`${BASE_URL}/select/countries?apikey=${API_KEY}&lang=pt`
);
const { success, data } = await response.json();
if (success) {
localStorage.setItem('countries', JSON.stringify(data));
populateCountrySelect(data);
}
}
function populateCountrySelect(countries) {
const select = document.getElementById('country');
countries.forEach(country => {
select.add(new Option(country.name, country.id));
});
}
Mostre feedback visual enquanto os dados carregam:
async function loadStates(countryId) {
const stateSelect = document.getElementById('state');
stateSelect.innerHTML = '<option>Carregando...</option>';
stateSelect.disabled = true;
// ... buscar estados
stateSelect.disabled = false;
}
Se você adicionar um dropdown pesquisável, use debounce na entrada:
let debounceTimer;
searchInput.addEventListener('input', (e) => {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => {
filterOptions(e.target.value);
}, 300);
});
Sempre forneça mensagens de erro amigáveis:
try {
// Chamada da API
} catch (err) {
const select = document.getElementById('state');
select.innerHTML = '<option>Erro ao carregar estados. Tente novamente.</option>';
}
A API suporta múltiplos idiomas. Altere o parâmetro lang:
// Espanhol
`${BASE_URL}/select/countries?apikey=${API_KEY}&lang=es`
// Português
`${BASE_URL}/select/countries?apikey=${API_KEY}&lang=pt`
// Francês
`${BASE_URL}/select/countries?apikey=${API_KEY}&lang=fr`
// Alemão
`${BASE_URL}/select/countries?apikey=${API_KEY}&lang=de`
// Italiano
`${BASE_URL}/select/countries?apikey=${API_KEY}&lang=it`
Se você tiver dúvidas ou precisar de assistência:
Dica Profissional: Para aplicações em produção, considere implementar um componente dropdown personalizado com funcionalidade de pesquisa para lidar com países com muitos estados/cidades. Bibliotecas como Select2, Choices.js ou autocomplete personalizado podem melhorar significativamente a experiência do usuário.