Modern JavaScript dropdown enhancement library without jQuery dependencies
npm install vanilla-smart-selectEnglish | Português
Biblioteca moderna, completa, leve e poderosa de aprimoramento de dropdown escrita em vanilla JavaScript puro - sem necessidade de jQuery.



> Transforme elementos nativos em dropdowns poderosos e pesquisáveis com suporte AJAX, templates customizados, internacionalização e muito mais - tudo sem dependências.
- Recursos • Instalação • Início Rápido
- Documentação • Referência da API • Eventos
- Suporte a Navegadores • Contribuindo
---
Transforme este select básico:
``html`
Em um dropdown poderoso com:
- 🔍 Busca ao Vivo - Encontre opções instantaneamente enquanto digita
- ⌨️ Navegação por Teclado - Setas, Enter, Escape, Tab
- 🎯 Multi-Seleção - Selecione múltiplos itens com tags
- 🌐 Carregamento AJAX - Carregue dados de APIs dinamicamente
- 📱 Mobile Friendly - Interface otimizada para toque
- ♿ Acessível - Compatível com leitores de tela, em conformidade com WCAG
- 🎨 Customizável - Controle total sobre estilos e templates
Tudo com apenas 3 linhas de JavaScript!
---
- 🚀 Zero Dependências - Vanilla JavaScript puro, sem necessidade de jQuery
- 📦 Leve - ~50KB minificado, ~15KB gzipped
- ♿ Acessível - Compatível com WCAG 2.1 Nível AA com suporte ARIA completo
- 🔍 Busca Inteligente - Busca em tempo real com suporte a diacríticos
- 🌍 Internacionalização - Suporte multi-idioma (EN, PT-BR, ES) com detecção automática
- 🎨 Templates Customizados - Controle total sobre renderização de itens com HTML/CSS
- 🔄 Suporte AJAX - Carregamento de dados remotos com debounce e cache
- 📜 Scroll Infinito - Paginação automática para grandes conjuntos de dados
- 🏷️ Tagging - Crie novas opções dinamicamente
- ⌨️ Navegação por Teclado - Acessibilidade completa por teclado
- 🎯 Multi-Seleção - Selecione múltiplos itens com limites opcionais
- 📊 Optgroups - Organize opções em grupos
- 🎨 Temas - Fácil de customizar com variáveis CSS
- 📱 Responsivo - Funciona perfeitamente em desktop e mobile
- 🅱️ Compatível com Bootstrap - Funciona perfeitamente com Bootstrap 5
---
Problema: Elementos nativos
Solução: Vanilla Smart Select fornece funcionalidade poderosa de dropdown com:
- ✅ Zero Dependências - Vanilla JavaScript puro, sem jQuery ou outras bibliotecas
- ✅ Leve - Tamanho de bundle pequeno (~15KB gzipped) vs alternativas pesadas
- ✅ Arquitetura Moderna - Construído com ES6+ e padrões web modernos
- ✅ Melhor Performance - Renderização otimizada e cache inteligente
- ✅ Recursos Ricos - i18n, AJAX, tagging, templates customizados e muito mais
Perfeito para: Aplicações web modernas, projetos React/Vue/Angular, Progressive Web Apps e qualquer projeto que valorize performance e código limpo.
---
bash
npm install vanilla-smart-select
`$3
`bash
yarn add vanilla-smart-select
`$3
`html
`$3
`javascript
import VanillaSmartSelect from 'vanilla-smart-select';
import 'vanilla-smart-select/dist/vanilla-smart-select.min.css';
`---
🚀 Início Rápido
$3
`html
`$3
`html
`$3
`javascript
const select = new VanillaSmartSelect('#mySelect', {
searchable: true,
placeholder: 'Selecione uma linguagem...'
});
`É isso! Seu select agora está aprimorado com busca, navegação por teclado e melhor estilização.
---
💡 Exemplos de Uso Básico
$3
`html
`$3
`javascript
const select = new VanillaSmartSelect('#mySelect', {
data: [
{ id: 1, text: 'JavaScript' },
{ id: 2, text: 'Python' },
{ id: 3, text: 'Ruby', disabled: true },
{
text: 'Frontend',
children: [
{ id: 4, text: 'React' },
{ id: 5, text: 'Vue' },
{ id: 6, text: 'Angular' }
]
}
],
placeholder: 'Selecione uma linguagem...'
});
`---
📖 Documentação
$3
- Recursos Principais
- Seleção Única
- Multi-Seleção
- Busca e Filtragem
- Optgroups
- Recursos Avançados
- Internacionalização (i18n)
- Templates Customizados
- Carregamento AJAX
- Tagging
- Paginação / Scroll Infinito
- Referência da API
- Eventos
- Opções de Configuração---
Recursos Principais
$3
Dropdown simples com busca e botão de limpar:
`javascript
const select = new VanillaSmartSelect('#single-select', {
placeholder: 'Escolha uma opção...',
allowClear: true,
searchable: true
});
`$3
Selecione múltiplas opções com limite opcional de seleção:
`javascript
const select = new VanillaSmartSelect('#multi-select', {
multiple: true,
placeholder: 'Selecione múltiplos itens...',
maximumSelectionLength: 5 // Limite opcional
});
`$3
Busca poderosa com suporte a diacríticos e matchers customizados:
`javascript
const select = new VanillaSmartSelect('#searchable-select', {
searchable: true,
searchMinimumLength: 2,
searchDelay: 250,
matchStrategy: 'contains', // 'startsWith' | 'contains' | 'exact' // Matcher customizado
matcher: (params, item) => {
const term = params.term.toLowerCase();
const text = item.text.toLowerCase();
return text.includes(term);
}
});
`$3
Organize opções em grupos:
`javascript
const select = new VanillaSmartSelect('#grouped-select', {
data: [
{
text: 'Frutas',
children: [
{ id: 'apple', text: 'Maçã' },
{ id: 'banana', text: 'Banana' }
]
},
{
text: 'Vegetais',
children: [
{ id: 'carrot', text: 'Cenoura' },
{ id: 'lettuce', text: 'Alface' }
]
}
]
});
`---
Recursos Avançados
$3
Suporte integrado para múltiplos idiomas com detecção automática:
`javascript
import { getLanguage } from 'vanilla-smart-select/i18n';// Detectar idioma do navegador automaticamente (EN, PT-BR ou ES)
const select = new VanillaSmartSelect('#i18n-select'); // Detecta automaticamente
// Ou definir manualmente
const select = new VanillaSmartSelect('#i18n-select', {
language: getLanguage('pt-BR')
});
// Alterar idioma dinamicamente
select.updateLanguage(getLanguage('es'));
`Idiomas Suportados:
- 🇺🇸 Inglês (en)
- 🇧🇷 Português - Brasil (pt-BR)
- 🇪🇸 Espanhol (es)
Mensagens Customizadas:
`javascript
const select = new VanillaSmartSelect('#select', {
language: {
noResults: 'Nenhum resultado encontrado',
searching: 'Buscando...',
loading: 'Carregando...',
errorLoading: 'Erro ao carregar resultados',
inputTooShort: (args) => Digite mais ${args.minimum} caracteres,
maximumSelected: (args) => Máximo de ${args.maximum} itens
}
});
`$3
Controle total sobre como os itens são renderizados:
`javascript
const select = new VanillaSmartSelect('#custom-template', {
data: [
{
id: 1,
text: 'João Silva',
avatar: 'https://i.pravatar.cc/32?img=1',
email: 'joao@exemplo.com'
}
], // Customizar itens do dropdown
templateResult: (item) => {
if (item._isTag) {
return
;
} const div = document.createElement('div');
div.style.display = 'flex';
div.style.gap = '10px';
div.innerHTML =
;
return div;
}, // Customizar itens selecionados
templateSelection: (item) => {
return
;
}
});
`Funções de Template:
- templateResult - Customiza itens na lista dropdown
- templateSelection - Customiza exibição de itens selecionados
- Retorno:
HTMLElement ou string (HTML)$3
Carregue dados de fontes remotas com debouncing e cache:
`javascript
const select = new VanillaSmartSelect('#ajax-select', {
placeholder: 'Buscar repositórios do GitHub...',
ajax: {
url: 'https://api.github.com/search/repositories',
method: 'GET',
delay: 300, // Delay de debounce
cache: true, // Habilitar cache // Transformar parâmetros da requisição
data: (params) => ({
q: params.term,
page: params.page,
per_page: 10
}),
// Processar resposta
processResults: (data, params) => ({
results: data.items.map(repo => ({
id: repo.id,
text: repo.full_name,
description: repo.description,
stars: repo.stargazers_count
})),
pagination: {
more: data.items.length >= 10
}
}),
// Headers customizados
headers: {
'Authorization': 'Bearer SEU_TOKEN'
},
// Transport customizado (opcional)
transport: (params, config) => {
return fetch(config.url + '?' + new URLSearchParams(params))
.then(r => r.json());
}
}
});
`Eventos AJAX:
`javascript
element.addEventListener('vs:ajaxLoading', (e) => {
console.log('Carregando...', e.detail.params);
});element.addEventListener('vs:ajaxSuccess', (e) => {
console.log('Carregado:', e.detail.results);
});
element.addEventListener('vs:ajaxError', (e) => {
console.error('Erro:', e.detail.error);
});
`$3
Permita que usuários criem novas opções dinamicamente:
`javascript
const select = new VanillaSmartSelect('#tags-select', {
tags: true,
multiple: true,
placeholder: 'Digite para adicionar tags...', // Validar e criar tag
createTag: (params) => {
const term = params.term.trim();
if (!term) return null;
// Exemplo de validação de email
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(term)) {
return null;
}
return {
id: term,
text: term
};
},
// Controlar posição de inserção da tag
insertTag: (data, tag) => {
data.unshift(tag); // Adicionar no início
},
// Template customizado para opção de criação de tag
templateResult: (item) => {
if (item._isTag) {
return
;
}
return item.text;
}
});
`Casos de Uso:
- Destinatários de email
- Palavras-chave/Categorias
- Menções de usuário (@usuario)
- Filtros customizados
$3
Carregue automaticamente mais resultados conforme o usuário rola:
`javascript
const select = new VanillaSmartSelect('#pagination-select', {
ajax: {
url: 'https://api.github.com/search/users',
data: (params) => ({
q: params.term || 'john',
page: params.page || 1,
per_page: 15
}),
processResults: (data, params) => ({
results: data.items.map(user => ({
id: user.id,
text: user.login,
avatar: user.avatar_url
})),
pagination: {
more: data.items.length >= 15 // Tem mais páginas
}
})
}
});
`Como funciona:
- Role até o final → carrega automaticamente a próxima página
- Resultados são acumulados (página 1 + página 2 + página 3...)
- Indicador "Carregando mais..." mostrado durante o carregamento
- Mudança no termo de busca reseta a paginação
---
Referência da API
$3
#### Gerenciamento de Valor
`javascript
// Obter valor atual
const value = select.val();// Definir valor (seleção única)
select.val('option-id');
// Definir valor (multi-seleção)
select.val(['id1', 'id2', 'id3']);
// Limpar seleção
select.clear();
`#### Seleção Programática
`javascript
// Selecionar item por ID
select.select('item-id');// Desselecionar item (apenas multi-seleção)
select.unselect('item-id');
// Obter item(s) selecionado(s) com dados completos
const selected = select.getSelected();
// Retorna: { id, text, ...customData } ou [{ id, text }, ...]
`#### Gerenciamento de Dados
`javascript
// Obter todos os dados
const data = select.data();// Definir/Substituir dados
select.data([
{ id: 1, text: 'Nova Opção 1' },
{ id: 2, text: 'Nova Opção 2' }
]);
// Adicionar opção única
select.addOption({ id: 'new', text: 'Nova Opção' });
// Remover opção por ID
select.removeOption('option-id');
`#### Controle do Dropdown
`javascript
// Abrir dropdown
select.open();// Fechar dropdown
select.close();
// Alternar dropdown
select.toggle();
// Verificar se está aberto
const isOpen = select.isOpen(); // boolean
// Focar no select
select.focus();
`#### Habilitar/Desabilitar
`javascript
// Desabilitar select
select.disable();// Habilitar select
select.enable();
`#### Validação HTML5
`javascript
// Verificar se é válido
const isValid = select.checkValidity(); // boolean// Mostrar mensagem de validação
const isValid = select.reportValidity(); // boolean
// Definir erro customizado
select.setCustomValidity('Por favor, selecione uma opção');
select.setCustomValidity(''); // Limpar erro
// Obter mensagem de validação
const message = select.validationMessage();
// Verificar se será validado
const willValidate = select.willValidate();
`#### Internacionalização
`javascript
import { getLanguage } from 'vanilla-smart-select/i18n';// Alterar idioma dinamicamente
select.updateLanguage(getLanguage('pt-BR'));
`#### Limpeza
`javascript
// Destruir instância e limpar
select.destroy();
`---
Eventos
Todos os eventos são prefixados com
vs: para evitar conflitos.$3
`javascript
const element = document.querySelector('#mySelect');// Eventos de seleção
element.addEventListener('vs:select', (e) => {
console.log('Selecionado:', e.detail.data);
});
element.addEventListener('vs:unselect', (e) => {
console.log('Desselecionado:', e.detail.data);
});
element.addEventListener('vs:change', (e) => {
console.log('Valor alterado:', e.detail.value);
});
element.addEventListener('vs:clear', (e) => {
console.log('Seleção limpa');
});
// Eventos do dropdown
element.addEventListener('vs:open', () => {
console.log('Dropdown aberto');
});
element.addEventListener('vs:close', () => {
console.log('Dropdown fechado');
});
// Eventos de busca
element.addEventListener('vs:query', (e) => {
console.log('Termo de busca:', e.detail.term);
});
element.addEventListener('vs:results', (e) => {
console.log('Resultados:', e.detail.results);
});
// Eventos de ciclo de vida
element.addEventListener('vs:init', () => {
console.log('Inicializado');
});
element.addEventListener('vs:destroy', () => {
console.log('Destruído');
});
`$3
`javascript
// Limite de seleção (multi-seleção)
element.addEventListener('vs:selectionLimitReached', (e) => {
console.log('Limite atingido:', e.detail.maximum);
console.log('Mensagem:', e.detail.message);
});// Eventos AJAX
element.addEventListener('vs:ajaxLoading', (e) => {
console.log('Carregando dados...', e.detail.params);
});
element.addEventListener('vs:ajaxSuccess', (e) => {
console.log('Dados carregados:', e.detail.results);
});
element.addEventListener('vs:ajaxError', (e) => {
console.error('Erro ao carregar dados:', e.detail.error);
});
// Eventos de dados
element.addEventListener('vs:dataLoaded', (e) => {
console.log('Dados carregados:', e.detail.data);
});
`$3
`javascript
// Estes eventos podem ser prevenidos com e.preventDefault()element.addEventListener('vs:selecting', (e) => {
if (shouldPrevent) {
e.preventDefault(); // Cancelar seleção
}
});
element.addEventListener('vs:unselecting', (e) => {
e.preventDefault(); // Cancelar desseleção
});
element.addEventListener('vs:clearing', (e) => {
e.preventDefault(); // Cancelar limpeza
});
element.addEventListener('vs:opening', (e) => {
e.preventDefault(); // Prevenir abertura do dropdown
});
element.addEventListener('vs:closing', (e) => {
e.preventDefault(); // Prevenir fechamento do dropdown
});
`---
Opções de Configuração
Lista completa de todas as opções disponíveis:
`javascript
{
// ===== Opções de Exibição =====
placeholder: '', // Texto placeholder
theme: 'default', // Nome do tema
width: '100%', // Largura do select
containerCssClass: '', // Classe CSS customizada para container
dropdownCssClass: '', // Classe CSS customizada para dropdown // ===== Opções de Comportamento =====
multiple: false, // Habilitar multi-seleção
searchable: true, // Habilitar busca
allowClear: false, // Mostrar botão limpar (seleção única)
disabled: false, // Desabilitar select
closeOnSelect: true, // Fechar dropdown após seleção
// ===== Opções de Dados =====
data: null, // Array de dados (alternativa a elementos
// ===== Opções de Busca =====
searchMinimumLength: 0, // Caracteres mínimos para buscar
searchDelay: 250, // Delay de debounce (ms)
searchPlaceholder: null, // Placeholder do input de busca
matcher: null, // Função matcher customizada
matchStrategy: 'contains', // 'startsWith' | 'contains' | 'exact'
// ===== Opções de Template =====
templateResult: null, // Função: (item) => HTMLElement | string
templateSelection: null, // Função: (item) => HTMLElement | string
escapeMarkup: (markup) => markup, // Função para sanitizar HTML
// ===== Opções do Dropdown =====
dropdownParent: null, // Elemento pai para o dropdown
dropdownAutoWidth: false, // Largura automática do dropdown
// ===== Opções AJAX =====
ajax: null, // Objeto de configuração AJAX
/*
ajax: {
url: '', // URL da API
method: 'GET', // Método HTTP
dataType: 'json', // Tipo de resposta: 'json' | 'text' | 'blob'
delay: 250, // Delay de debounce (ms)
cache: false, // Habilitar cache
headers: {}, // Headers customizados
data: (params) => params, // Transformar parâmetros da requisição
processResults: (data) => ({ results: data }), // Processar resposta
transport: null // Função fetch customizada
}
*/
// ===== Opções de Tagging =====
tags: false, // Habilitar tagging
createTag: (params) => { // Função criar tag
const term = params.term?.trim();
if (!term) return null;
return { id: term, text: term };
},
insertTag: (data, tag) => { // Função inserir tag
data.unshift(tag);
},
// ===== Opções de Idioma/i18n =====
language: { // Objeto de idioma ou detecção automática
noResults: 'Nenhum resultado encontrado',
searching: 'Buscando...',
searchPlaceholder: 'Buscar...',
loading: 'Carregando...',
loadingMore: 'Carregando mais resultados...',
errorLoading: 'Os resultados não puderam ser carregados',
inputTooShort: (args) =>
Por favor, digite ${args.minimum} ou mais caracteres,
inputTooLong: (args) => Por favor, delete ${args.excess} caracteres,
maximumSelected: (args) => Você só pode selecionar ${args.maximum} itens,
createNewTag: (args) => Criar tag: "${args.term}",
loadMore: 'Carregar mais resultados'
}, // ===== Opções de Acessibilidade =====
ariaLabel: null, // Label ARIA
ariaDescribedBy: null, // ARIA described by
// ===== Opções de Seleção =====
maximumSelectionLength: 0, // Seleções máximas (0 = ilimitado, apenas multi-seleção)
// ===== Callbacks de Eventos =====
onOpen: null, // Função chamada ao abrir
onClose: null, // Função chamada ao fechar
onChange: null, // Função chamada ao alterar valor
onSelect: null, // Função chamada ao selecionar item
onUnselect: null, // Função chamada ao desselecionar item
onClear: null, // Função chamada ao limpar
// ===== Debug =====
debug: false // Habilitar modo debug
}
`---
Suporte a Navegadores
- ✅ Chrome/Edge (últimas 2 versões)
- ✅ Firefox (últimas 2 versões)
- ✅ Safari (últimas 2 versões)
- ✅ Opera (últimas 2 versões)
- ❌ Internet Explorer (não suportado)
Requisitos Mínimos:
- Suporte ES6+
- Fetch API
- CSS Grid
- CSS Custom Properties (variáveis)
---
Performance
- Tamanho do Bundle: ~50KB minificado, ~15KB gzipped
- Zero Dependências: Nenhuma biblioteca externa necessária
- Renderização Otimizada: Conceitos de Virtual DOM para performance
- Cache Inteligente: Respostas AJAX cacheadas automaticamente
- Busca com Debounce: Chamadas de API reduzidas
---
🚀 Melhorias Futuras
Ideias sendo consideradas para versões futuras:
- Virtual scrolling para 10k+ itens
- Drag & drop para reordenar seleções
- Sistema de temas avançado
- Definições TypeScript
- API de Plugin/Extensão
Sugestões? Abra uma discussão!
---
🤝 Contribuindo
Contribuições são bem-vindas! Seja correções de bugs, novos recursos, melhorias na documentação ou exemplos.
Como contribuir:
1. Faça um fork do repositório
2. Crie um branch de feature (
git checkout -b feature/recurso-incrivel)
3. Faça suas alterações
4. Teste minuciosamente (execute exemplos, verifique compatibilidade)
5. Commit suas alterações (git commit -m 'Adiciona recurso incrível')
6. Push para o branch (git push origin feature/recurso-incrivel)
7. Abra um Pull RequestÁreas onde ajuda é apreciada:
- 🐛 Correção de bugs
- 📝 Melhorias na documentação
- 🌍 Traduções de idiomas adicionais
- 💡 Novos exemplos
- 🎨 Temas
- ✅ Testes
$3
`bash
Clonar o repositório
git clone https://github.com/AiltonOcchi/vanilla-smart-select.git
cd vanilla-smart-selectInstalar dependências
npm installIniciar modo de desenvolvimento (rebuild automático em alterações)
npm run devBuild para produção
npm run buildExecutar testes
npm test
``---
Licença MIT - veja o arquivo LICENSE para detalhes
---
- 📚 Documentação: Você está lendo!
- 🐛 Relatórios de Bug: GitHub Issues
- 💡 Solicitações de Recursos: GitHub Discussions
- ⭐ Mostre Apoio: Dê uma estrela ao projeto no GitHub!
---