Pick List / Dual Listbox: De jQuery Sortable a Drag-and-Drop Headless
Minha resposta no Stack Overflow sobre construir um pick list dual listbox recebeu 5 votos. Em 2026, bibliotecas headless UI e a API nativa de drag-and-drop tornam esse padrao acessivel por padrao.
Pick List / Dual Listbox: De jQuery Sortable a Drag-and-Drop Headless
Respondi uma pergunta no Stack Overflow em Portugues sobre construir um pick list — aquele padrao de dual listbox onde usuarios movem itens entre duas colunas. Recebeu 5 votos, porque esse componente de UI aparecia em todo app enterprise e ninguem encontrava um jeito limpo de construi-lo.
A solucao funcionava com o que tinhamos na epoca. Mas o ferramental mudou tanto que o mesmo padrao agora e quase trivial de implementar corretamente.
O Problema de 2016: jQuery UI Sortable e Manipulacao Manual de DOM
A abordagem padrao era jQuery UI Sortable com listas conectadas:
// 2016: Listas sortable conectadas com jQuery UI
$('#available, #selected')
.sortable({
connectWith: '.connectable',
placeholder: 'ui-state-highlight',
receive: function (event, ui) {
updateHiddenField();
},
})
.disableSelection();
<ul id="available" class="connectable">
<li>Item A</li>
<li>Item B</li>
</ul>
<button onclick="moveSelected()">→</button>
<button onclick="moveBack()">←</button>
<ul id="selected" class="connectable"></ul>
Voce tambem precisava de botoes de seta para usuarios que nao sabiam que podiam arrastar. E a funcao moveSelected() era sempre uma bagunca — clonando nos DOM manualmente, removendo originais, atualizando campos hidden que realmente enviavam os dados.
O Que Tornava Doloroso
O dual listbox era uma tempestade perfeita de desafios de UI:
- Acessibilidade era secundaria — jQuery UI Sortable nao tinha suporte ARIA. Leitores de tela nao conseguiam informar o usuario do que estava acontecendo durante operacoes de arraste
- Estado vivia no DOM — A fonte de verdade era a lista de elementos
<li>, nao uma estrutura de dados JavaScript. Cada operacao era uma mutacao no DOM - Mobile era quebrado — jQuery UI Sortable nao suportava eventos touch nativamente. Voce precisava do jquery.ui.touch-punch.js, que era um hack em cima de outro hack
- Submissao de formulario era fragil — Voce tinha que sincronizar a lista visivel com inputs hidden a cada mudanca, e torcer para nada ficar fora de sincronia
Vi implementacoes onde a atualizacao do campo hidden falhava silenciosamente, e usuarios organizavam cuidadosamente suas selecoes so para submeter uma lista vazia. Bons tempos.
A Abordagem de 2026: Headless UI e APIs Nativas
Componentes Listbox Headless
Bibliotecas de UI modernas separam comportamento de apresentacao:
// 2026: Dual listbox headless com acessibilidade adequada
function DualListbox({ options, selected, onChange }) {
return (
<div role="group" aria-label="Selecao de itens">
<Listbox
aria-label="Itens disponiveis"
items={options.filter((o) => !selected.includes(o.id))}
onSelect={(items) => onChange([...selected, ...items])}
/>
<div role="toolbar">
<button aria-label="Mover selecionados para escolhidos">→</button>
<button aria-label="Remover selecionados dos escolhidos">←</button>
</div>
<Listbox
aria-label="Itens selecionados"
items={options.filter((o) => selected.includes(o.id))}
onSelect={(items) => onChange(selected.filter((s) => !items.includes(s)))}
/>
</div>
);
}
O estado vive no JavaScript. A UI e uma projecao desse estado. Sem manipulacao de DOM.
Drag and Drop Nativo com ARIA Live Regions
<!-- 2026: ARIA live region anuncia operacoes de arraste -->
<div aria-live="polite" class="sr-only" id="drag-announce"></div>
// Anuncia operacoes de arraste para leitores de tela
element.addEventListener('dragstart', () => {
document.getElementById('drag-announce').textContent =
`Arrastando ${item.name}. Solte na lista selecionada para adicionar.`;
});
A API nativa de Drag and Drop agora funciona em dispositivos touch. Combinada com ARIA live regions, usuarios de leitores de tela recebem feedback em tempo real sobre o que esta acontecendo.
O Que Mudou
O padrao do pick list em si nao mudou — usuarios ainda movem itens entre duas colunas. O que mudou e que o gerenciamento de estado migrou do DOM para o JavaScript, acessibilidade se tornou uma preocupacao de primeira classe ao inves de um retrofit, e suporte touch vem de graca com APIs da plataforma.
Aquela resposta no Stack Overflow resolveu o problema de implementacao imediato. Mas a licao maior e que padroes interativos complexos so se tornam confiaveis quando o modelo de dados e separado da apresentacao.
Posts Relacionados
Pick List / Dual Listbox: De jQuery Sortable a Drag-and-Drop Headless
Minha resposta no Stack Overflow sobre construir um pick list dual listbox recebeu 5 votos. Em 2026, bibliotecas headless UI e a API nativa de drag-and-drop tornam esse padrao acessivel por padrao.
Adicionar aos Favoritos: De APIs do Navegador a PWA Install
Minha resposta no SO de 2015 mostrava window.external.AddFavorite() para IE. Em 2026, navegadores removeram bookmarking programático — mas PWA Install substituiu.
Bootstrap Modal + Tooltip: Da Guerra de Plugins jQuery ao HTML Nativo
Minha resposta no Stack Overflow de 2015 resolveu conflitos entre modal e tooltip do Bootstrap no mesmo elemento. Em 2026, dialog e popover nativos tornam o Bootstrap opcional.