Você está cansado dos campos select padrão do HTML que parecem ter saído de uma máquina do tempo? Aqueles que não oferecem busca, têm um estilo limitado e são péssimos para selecionar múltiplos itens? Se a resposta é sim, você veio ao lugar certo!
Neste artigo, vamos transformar seus selects comuns em dropdowns modernos, pesquisáveis e com suporte a multi-seleção, usando um pouco de CSS e JavaScript. Diga adeus à experiência de usuário frustrante e olá a formulários mais intuitivos e bonitos no seu site!
Para que Serve este Código?
Este snippet de código HTML, CSS e JavaScript tem um propósito claro: melhorar a usabilidade e a estética dos elementos <select> nativos do HTML.
Ele faz isso ao:
- Customizar a aparência: Aplica um design moderno e responsivo, muito mais agradável visualmente do que o select padrão do navegador.
- Adicionar funcionalidade de busca: Permite que os usuários digitem no campo para filtrar as opções, tornando a seleção mais rápida, especialmente em listas longas.
- Habilitar multi-seleção aprimorada: Para os selects que precisam permitir a escolha de múltiplos itens, ele exibe os itens selecionados como “tags” visíveis, que podem ser facilmente removidas.
- Manter a compatibilidade: Embora seja um componente visualmente customizado, ele ainda se baseia no elemento
<select>original, garantindo que os dados sejam enviados corretamente em formulários e que a acessibilidade básica seja mantida.
É perfeito para formulários de cadastro, filtros de produtos, seletores de categorias, ou qualquer lugar onde você precise de um dropdown mais poderoso e amigável.
Como Usar este Código no seu Site (Elementor ou Bricks)
A aplicação deste código é bastante direta e pode ser feita facilmente usando recursos de customização de temas ou construtores de página como Elementor e Bricks.
Passo 1: Prepare seu HTML
Primeiro, você precisará ter um elemento <select> no seu HTML. A magia acontece com as classes CSS que você adiciona a ele:
- Para um dropdown comum (seleção única), adicione a classe
auto-select. - Para um dropdown com multi-seleção, adicione a classe
auto-select-mult.
Exemplo de HTML para Seleção Única:
<label for="cidade">Selecione sua cidade:</label>
<select id="cidade" class="auto-select">
<option value="">Selecione...</option>
<option value="saopaulo">São Paulo</option>
<option value="riodejaneiro">Rio de Janeiro</option>
<option value="belohorizonte">Belo Horizonte</option>
<option value="curitiba">Curitiba</option>
<option value="portoalegre">Porto Alegre</option>
</select>
Exemplo de HTML para Multi-Seleção:
<label for="interesses">Selecione seus interesses:</label>
<select id="interesses" class="auto-select-mult" multiple>
<option value="tecnologia">Tecnologia</option>
<option value="design">Design</option>
<option value="marketing">Marketing</option>
<option value="negocios">Negócios</option>
<option value="inovacao">Inovação</option>
<option value="educacao">Educação</option>
</select>
Importante: O atributo multiple é crucial no seu <select> original para que a funcionalidade de multi-seleção funcione corretamente com a classe auto-select-mult.
Passo 2: Inserir o Código (CSS e JavaScript)
Agora, você precisa adicionar o CSS e o JavaScript fornecidos ao seu site. A melhor forma de fazer isso, especialmente com Elementor ou Bricks, é através de um Snippet de Código ou um Bloco de Código/HTML Customizado.
Usando no Elementor:
- Vá para Elementor > Custom Code no seu painel WordPress.
- Clique em “Add New Custom Code”.
- Dê um título (ex: “Dropdown Personalizado”).
- No campo de código, cole todo o CSS e o JavaScript juntos, exatamente como fornecido.
- Para o CSS: Envolva o código CSS com as tags
<style>e</style>. - Para o JavaScript: Envolva o código JavaScript com as tags
<script>e</script>.
- Para o CSS: Envolva o código CSS com as tags
- Defina a localização para
</body>(End Body) para o JavaScript ser carregado após o HTML. - Defina a prioridade como
10ou superior para garantir que ele seja carregado. - Defina a condição de exibição para
Entire Site(ou páginas específicas onde você usará os selects). - Publique o código.
Usando no Bricks Builder:
- Vá para Bricks > Settings > Custom Code no seu painel WordPress.
- Role para a seção “Custom CSS” e cole o código CSS dentro do campo.
- Role para a seção “Custom Javascript” e cole o código JavaScript dentro do campo.
- Alternativamente, se você quiser manter tudo em um único lugar, pode usar um elemento de “Code” dentro do Bricks Builder diretamente na página ou template onde o select será usado.
- Arraste um elemento “Code” para a sua tela.
- No painel de configurações do elemento, selecione
HTMLe cole todo o código (CSS dentro de<style>e JS dentro de<script>). - Recomendação: Para performance e organização, preferimos a abordagem de “Custom Code” globalmente, mas para testes ou uso em páginas muito específicas, o elemento “Code” funciona.
Observações Finais
- Personalização: As variáveis CSS no
:root(cores, bordas, etc.) são facilmente ajustáveis para combinar com a paleta de cores do seu site. - Acessibilidade: Embora este código melhore a UX, é sempre bom testar a acessibilidade com leitores de tela para garantir uma experiência ótima para todos os usuários.
- Conflitos: Em casos raros, pode haver conflitos com outros scripts ou estilos em seu site. Se algo não funcionar como esperado, verifique o console do navegador em busca de erros.
Com este código, seus formulários não serão mais os mesmos! Comece a implementar e ofereça uma experiência de seleção de opções superior aos seus visitantes.
Espero que este artigo seja útil para o seu blog!
Código para a funcionalidade:
CSS
:root {
--primary-color: #4361ee;
--hover-color: #3a56d4;
--text-color: #333;
--light-gray: #f8f9fa;
--medium-gray: #e9ecef;
--dark-gray: #6c757d;
--border-radius: 8px;
--box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
--transition: all 0.3s ease;
}
.custom-select {
position: relative;
width: 100%;
font-family: 'Segoe UI', Roboto, sans-serif;
}
.select-input {
width: 100%;
padding: 12px 16px;
border: 2px solid var(--medium-gray);
border-radius: var(--border-radius);
box-sizing: border-box;
cursor: pointer;
background-color: white;
font-size: 14px;
color: var(--text-color);
transition: var(--transition);
display: flex;
align-items: center;
justify-content: space-between;
}
.select-input:hover {
border-color: var(--primary-color);
}
.select-input::after {
content: '▼';
font-size: 10px;
color: var(--dark-gray);
transition: var(--transition);
}
.select-input.open::after {
transform: rotate(180deg);
color: var(--primary-color);
}
.dropdown-container {
display: none;
position: absolute;
width: 100%;
top: 100%;
left: 0;
z-index: 1000;
background: white;
border-radius: 0 0 var(--border-radius) var(--border-radius);
box-shadow: var(--box-shadow);
border: 2px solid var(--primary-color);
border-top: none;
overflow: hidden;
}
.search-container {
padding: 12px;
}
.search-input {
width: 100%;
padding: 10px 12px;
border: 1px solid var(--medium-gray);
border-radius: 4px;
box-sizing: border-box;
font-size: 14px;
transition: var(--transition);
}
.search-input:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 2px rgba(67, 97, 238, 0.2);
}
.options-container {
max-height: 250px;
overflow-y: auto;
}
.option-item {
padding: 12px 16px;
cursor: pointer;
font-size: 14px;
color: var(--text-color);
transition: var(--transition);
border-bottom: 1px solid var(--medium-gray);
}
.option-item:last-child {
border-bottom: none;
}
.option-item:hover {
background-color: var(--light-gray);
color: var(--primary-color);
}
.selected-items {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 8px;
}
.selected-item {
background-color: var(--primary-color);
color: white;
padding: 6px 12px;
border-radius: 20px;
display: flex;
align-items: center;
font-size: 13px;
transition: var(--transition);
}
.selected-item:hover {
background-color: var(--hover-color);
}
.remove-item {
margin-left: 8px;
cursor: pointer;
color: white;
font-size: 14px;
line-height: 1;
}
.remove-item:hover {
color: #ffcccb;
}
.no-results {
padding: 12px 16px;
color: var(--dark-gray);
font-size: 14px;
text-align: center;
}
/* Scrollbar personalizada */
.options-container::-webkit-scrollbar {
width: 8px;
}
.options-container::-webkit-scrollbar-track {
background: var(--light-gray);
border-radius: 0 0 var(--border-radius) 0;
}
.options-container::-webkit-scrollbar-thumb {
background: var(--dark-gray);
border-radius: 4px;
}
.options-container::-webkit-scrollbar-thumb:hover {
background: #555;
}
JS
document.addEventListener('DOMContentLoaded', function() {
// Função para inicializar selects personalizados
function initializeCustomSelects(selector) {
const elements = document.querySelectorAll(selector);
elements.forEach(element => {
if (element.tagName === 'SELECT') {
// Determina se é multi-seleção
const isMultiSelect = element.classList.contains('auto-select-mult');
createCustomSelect(element, isMultiSelect);
}
});
}
// Cria um select personalizado
function createCustomSelect(selectElement, isMultiSelect) {
// Criar container principal
const container = document.createElement('div');
container.className = 'custom-select';
// Criar input para mostrar os itens selecionados
const input = document.createElement('div');
input.className = 'select-input';
input.textContent = 'Selecione...';
input.tabIndex = 0;
// Criar container para itens selecionados (apenas para multi-select)
const selectedItemsContainer = document.createElement('div');
selectedItemsContainer.className = 'selected-items';
if (!isMultiSelect) {
selectedItemsContainer.style.display = 'none'; // Oculta para single-select
}
// Criar container dropdown (busca + opções)
const dropdownContainer = document.createElement('div');
dropdownContainer.className = 'dropdown-container';
// Criar container para o campo de busca
const searchContainer = document.createElement('div');
searchContainer.className = 'search-container';
// Criar input de busca
const searchInput = document.createElement('input');
searchInput.className = 'search-input';
searchInput.placeholder = 'Digite aqui para buscar...';
searchInput.type = 'text';
searchContainer.appendChild(searchInput);
// Criar container de opções
const optionsContainer = document.createElement('div');
optionsContainer.className = 'options-container';
// Montar a estrutura
dropdownContainer.appendChild(searchContainer);
dropdownContainer.appendChild(optionsContainer);
// Adicionar elementos ao container principal
container.appendChild(input);
container.appendChild(selectedItemsContainer);
container.appendChild(dropdownContainer);
// Inserir após o select original e escondê-lo
selectElement.style.display = 'none';
selectElement.parentNode.insertBefore(container, selectElement.nextSibling);
// Carregar opções
loadOptions(selectElement, optionsContainer, selectedItemsContainer, isMultiSelect, input);
// Event listeners
input.addEventListener('click', function() {
toggleSelect(container, dropdownContainer, searchInput);
});
input.addEventListener('keydown', function(e) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
toggleSelect(container, dropdownContainer, searchInput);
}
});
searchInput.addEventListener('input', function() {
filterOptions(this.value.toLowerCase(), optionsContainer);
});
searchInput.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
closeSelect(container, dropdownContainer);
}
});
// Impede o fechamento ao clicar no dropdown ou no container principal
dropdownContainer.addEventListener('click', function(e) {
e.stopPropagation();
});
container.addEventListener('click', function(e) {
e.stopPropagation();
});
// Manter o fechamento ao clicar fora do componente custom-select
document.addEventListener('click', function(e) {
if (!container.contains(e.target)) {
closeSelect(container, dropdownContainer);
}
});
}
// Abre/fecha o select
function toggleSelect(container, dropdownContainer, searchInput) {
const isOpen = container.classList.contains('open');
// Fechar outros selects abertos, se houver
document.querySelectorAll('.custom-select.open').forEach(openSelect => {
if (openSelect !== container) {
closeSelect(openSelect, openSelect.querySelector('.dropdown-container'));
}
});
if (isOpen) {
closeSelect(container, dropdownContainer);
} else {
openSelect(container, dropdownContainer, searchInput);
}
}
function openSelect(container, dropdownContainer, searchInput) {
container.classList.add('open');
container.querySelector('.select-input').classList.add('open');
dropdownContainer.style.display = 'block';
searchInput.focus();
}
function closeSelect(container, dropdownContainer) {
if (!container || !dropdownContainer) return;
container.classList.remove('open');
const input = container.querySelector('.select-input');
if (input) input.classList.remove('open');
dropdownContainer.style.display = 'none';
// Limpar o campo de busca ao fechar
const searchInput = container.querySelector('.search-input');
if (searchInput) {
searchInput.value = '';
filterOptions('', container.querySelector('.options-container')); // Resetar filtro
}
}
// Carrega as opções do select original
function loadOptions(selectElement, optionsContainer, selectedItemsContainer, isMultiSelect, inputDisplay) {
const options = Array.from(selectElement.options);
renderOptions(options, optionsContainer, selectedItemsContainer, selectElement, isMultiSelect, inputDisplay);
// Inicializar o texto de exibição para single-select
if (!isMultiSelect) {
const selectedOption = options.find(opt => opt.selected);
if (selectedOption) {
inputDisplay.textContent = selectedOption.text;
}
}
}
// Renderiza as opções na lista
function renderOptions(options, optionsContainer, selectedItemsContainer, selectElement, isMultiSelect, inputDisplay) {
optionsContainer.innerHTML = '';
let selectedValues = [];
if (isMultiSelect) {
selectedValues = Array.from(selectedItemsContainer.querySelectorAll('.selected-item'))
.map(item => item.dataset.value);
} else {
// Para single-select, o valor selecionado é o texto do input
const selectedOption = Array.from(selectElement.options).find(opt => opt.selected);
if (selectedOption) {
selectedValues.push(selectedOption.value);
}
}
const availableOptions = options.filter(option =>
option.value && (isMultiSelect ? !selectedValues.includes(option.value) : true) // Multi: remove selecionados. Single: mostra todos.
);
if (availableOptions.length === 0) {
const noResults = document.createElement('div');
noResults.className = 'no-results';
noResults.textContent = 'Nenhuma opção disponível';
optionsContainer.appendChild(noResults);
return;
}
availableOptions.forEach(option => {
const optionElement = document.createElement('div');
optionElement.className = 'option-item';
optionElement.textContent = option.text;
optionElement.dataset.value = option.value;
// Para single-select, marque a opção selecionada com uma classe para estilização, se desejar
if (!isMultiSelect && option.selected) {
optionElement.classList.add('selected');
}
optionElement.addEventListener('click', function(e) {
e.stopPropagation(); // Impede o fechamento ao selecionar um item
if (isMultiSelect) {
addSelectedItem(option.text, option.value, selectedItemsContainer, selectElement, optionsContainer);
} else {
// Para single-select, remove seleção anterior e adiciona a nova
const currentSelected = selectElement.querySelector('option:checked');
if (currentSelected) {
currentSelected.selected = false;
}
updateOriginalSelect(selectElement, option.value, true);
inputDisplay.textContent = option.text; // Atualiza o texto do input principal
closeSelect(this.closest('.custom-select'), this.closest('.dropdown-container')); // Fecha para single-select
}
// Re-renderiza as opções após a seleção (necessário para multi-select para remover o item)
renderOptions(Array.from(selectElement.options), optionsContainer, selectedItemsContainer, selectElement, isMultiSelect, inputDisplay);
const searchInput = selectElement.parentNode.querySelector('.search-input');
if (searchInput) {
filterOptions(searchInput.value.toLowerCase(), optionsContainer);
}
});
optionsContainer.appendChild(optionElement);
});
}
// Filtra opções baseado no termo de busca
function filterOptions(searchTerm, optionsContainer) {
const options = optionsContainer.querySelectorAll('.option-item');
let hasVisibleOptions = false;
options.forEach(option => {
if (option.classList.contains('no-results')) return;
const text = option.textContent.toLowerCase();
if (text.includes(searchTerm)) {
option.style.display = 'block';
hasVisibleOptions = true;
} else {
option.style.display = 'none';
}
});
// Mostrar mensagem se não houver resultados
let noResults = optionsContainer.querySelector('.no-results');
if (!hasVisibleOptions && !noResults) {
const message = document.createElement('div');
message.className = 'no-results';
message.textContent = 'Nenhum resultado encontrado';
optionsContainer.appendChild(message);
} else if (hasVisibleOptions && noResults) {
noResults.remove();
}
}
// Adiciona um item selecionado (apenas para multi-select)
function addSelectedItem(text, value, container, originalSelect, optionsContainer) {
// Verificar se já está selecionado
if (container.querySelector(`[data-value="${value}"]`)) {
return;
}
// Criar elemento do item selecionado
const item = document.createElement('div');
item.className = 'selected-item';
item.dataset.value = value;
const itemText = document.createElement('span');
itemText.textContent = text;
const removeBtn = document.createElement('span');
removeBtn.className = 'remove-item';
removeBtn.textContent = '×';
removeBtn.addEventListener('click', function(e) {
e.stopPropagation();
item.remove();
updateOriginalSelect(originalSelect, value, false);
// Ao remover, re-renderiza as opções para que o item removido apareça novamente se o filtro permitir
renderOptions(Array.from(originalSelect.options), optionsContainer, container, originalSelect, true, null); // isMultiSelect é true
// Manter o campo de busca com o texto atual
const searchInput = originalSelect.parentNode.querySelector('.search-input');
if (searchInput) {
filterOptions(searchInput.value.toLowerCase(), optionsContainer);
}
});
item.appendChild(itemText);
item.appendChild(removeBtn);
container.appendChild(item);
// Atualizar select original
updateOriginalSelect(originalSelect, value, true);
// Atualizar lista de opções (remover o item selecionado)
renderOptions(Array.from(originalSelect.options), optionsContainer, container, originalSelect, true, null); // isMultiSelect é true
// Manter o campo de busca com o texto atual após a seleção de um item
const searchInput = originalSelect.parentNode.querySelector('.search-input');
if (searchInput) {
filterOptions(searchInput.value.toLowerCase(), optionsContainer);
}
}
// Atualiza o select original (oculto)
function updateOriginalSelect(select, value, isAdding) {
const option = Array.from(select.options).find(opt => opt.value === value);
if (option) {
option.selected = isAdding;
}
// Disparar evento de change
const event = new Event('change');
select.dispatchEvent(event);
}
// Inicializar nos campos desejados (agora usando 'auto-select')
initializeCustomSelects('.auto-select');
});
