Chega de SELECT Monótono: Crie Dropdowns Incríveis com Busca e Multi-Seleção!

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:

  1. Vá para Elementor > Custom Code no seu painel WordPress.
  2. Clique em “Add New Custom Code”.
  3. Dê um título (ex: “Dropdown Personalizado”).
  4. 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>.
  5. Defina a localização para </body> (End Body) para o JavaScript ser carregado após o HTML.
  6. Defina a prioridade como 10 ou superior para garantir que ele seja carregado.
  7. Defina a condição de exibição para Entire Site (ou páginas específicas onde você usará os selects).
  8. Publique o código.

Usando no Bricks Builder:

  1. Vá para Bricks > Settings > Custom Code no seu painel WordPress.
  2. Role para a seção “Custom CSS” e cole o código CSS dentro do campo.
  3. Role para a seção “Custom Javascript” e cole o código JavaScript dentro do campo.
  4. 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 HTML e 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');
  });
Voltar ao Blog