Se você deseja aumentar a interatividade e melhorar o design dos seus formulários na web, aprender como criar um ComboBox com imagens usando HTML, CSS e JavaScript é um excelente passo. Embora o HTML sozinho não permita adicionar imagens diretamente nas opções de uma caixa de seleção, com a combinação de CSS e JavaScript é possível exibir imagens junto ao texto em cada opção, tornando o formulário mais visual e atraente. Neste tutorial, mostraremos um exemplo prático de como criar um ComboBox com imagens de forma simples e eficaz.

Primeiro, vamos criar um select (ComboBox) tradicional e adicionar um atributo data a cada option com a URL da imagem que será exibida. Esse atributo personalizado permitirá vincular uma imagem a cada opção do ComboBox.

<select name="pais" id="pais" style="display:none">
    <option value="BRA" data-image="imgs/BRA.png">Brasil</option>
    <option value="ARG" data-image="imgs/ARG.png">Argentina</option>
    <option value="URU" data-image="imgs/URU.png" selected>Uruguai</option>
</select>

Agora vamos para o JavaScript, onde iremos criar uma classe. O objetivo dessa classe é receber o ID do elemento <select> com suas options, ocultar esse select e, em seguida, criar dinamicamente um novo ComboBox utilizando divs que exibem as opções com imagens. Quando uma opção é selecionada no ComboBox personalizado com a imagem, o valor correspondente é automaticamente atualizado no select oculto. Esse processo melhora a experiência visual e mantém a funcionalidade do formulário original.

class ComboImage{

    constructor(comboId) {
        this.combo = document.getElementById(comboId);
        this.combo.style.display="none";

        this.comboContainer = document.createElement("div");
        this.comboContainer.className = "combo-image";
        this.combo.after(this.comboContainer);
        
        
        this._initDisplay();
        
        this.comboContainerOptions = document.createElement("div");
        this.comboContainerOptions.className = "combo-image-options";
        this.comboContainerOptions.style.display = "none";
        this.comboContainerOptions.style.width = this.comboContainerDisplay.offsetWidth+'px';
        console.log(this.comboContainerDisplay.offsetWidth )
                
        this._loadOptions();
        
        this.comboContainer.appendChild(this.comboContainerOptions);
    }

    _initDisplay() {
        this.comboContainerDisplay = document.createElement("div");
        this.comboContainerDisplay.className = "combo-image-display";
        this.comboContainer.appendChild(this.comboContainerDisplay);

        const selected = this.combo.options[this.combo.selectedIndex];
        this.comboContainerDisplay.innerHTML = `<img src="${selected.dataset.image}"> ${selected.text}`;
    
        this.comboContainerDisplay.addEventListener("click", () => {
            this._toggleOption();
        });    
    }

    _loadOptions() {
        Array.prototype.forEach.call( this.combo.options, (elemento) => {
            const divOption = document.createElement("div");
            const img = document.createElement("img");
            img.src = elemento.dataset.image;
            divOption.appendChild(img);
            divOption.appendChild(document.createTextNode(elemento.text));
            divOption.addEventListener("click", (ev) => {
                this._selectOption(elemento);
            });
            this.comboContainerOptions.appendChild(divOption);
        }) 
    }

    _toggleOption() {
        this.comboContainerOptions.style.display = this.comboContainerOptions.style.display =='none' 
                ? "block" 
                : "none";
    }

    _selectOption(elemento) {
        this.combo.value = elemento.value;
        this.comboContainerDisplay.innerHTML = `<img src="${elemento.dataset.image}"> ${elemento.text}`;
        this.comboContainerOptions.style.display = "none";
    }

}

Como vimos o código Javascript utiliza algumas classes CSS então vamos a elas:

Como visto anteriormente, o código JavaScript utiliza algumas classes CSS para estilizar o ComboBox personalizado. Agora, vamos detalhar essas classes.

body {
    font-family: Arial, Helvetica, sans-serif;
}
.combo-image img{
    width: 24px;
}
.combo-image-display{
    border: 1px solid #CCC;
    border-radius: 3px;
    padding: 5px;
    cursor: pointer; 
    width: 100%;  
    display: flex; 
    gap: 5px;
}
.combo-image-display::after{
    content:  url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6"><path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" /></svg>');
    float: right;
    width: 18px;
    height: 18px;
    margin-left: auto;
}

.combo-image-options{
    border: 1px solid #CCC;
    border-radius: 3px;
    padding: 5px;
    position: absolute;
    background-color: #FFF;
    width: 100%;
}

.combo-image-options > div{
    padding: 5px;
    cursor: pointer;
    display: flex;
    gap: 5px;
}
.combo-image-options > div:hover{   
    background-color: #CCC;
}

.combo-image-options img{
    width: 24px;
}
Exemplo de ComboBox com imagem

Veja um exemplo online

ComboBox com Imagem com AlpineJS e Tailwind

Seguindo essa abordagem anterior, podemos utilizar o Tailwind CSS para aplicar os estilos e Alpine.js para carregar o JavaScript. Vamos a um exemplo prático: primeiro, criaremos o arquivo combo-image.js para implementar a funcionalidade JavaScript.

function comboImage(el){
    const comboElement = el;
    comboElement.classList.add("hidden");

    const comboContainer = document.createElement("div");
    comboElement.after(comboContainer);
    
    const optionSelected = comboElement.options[comboElement.selectedIndex];
    const comboContainerDisplay = document.createElement("div");
    comboContainerDisplay.className = "flex w-full gap-2 p-1 border rounded-sm cursor-pointer";
    comboContainerDisplay.className += " after:content-['▾']  after:right after:ml-auto ";
    
    comboContainerDisplay.innerHTML = `
        <img src="${optionSelected.dataset.image}" class="w-8"> 
        ${optionSelected.text}`;
    comboContainer.appendChild(comboContainerDisplay);

    const comboContainerOptions = document.createElement("div");
    comboContainerOptions.className = 'absolute hidden w-full p-2 bg-white border rounded-sm';
    comboContainerOptions.style.width = comboContainerDisplay.offsetWidth+'px';

    Array.prototype.forEach.call(comboElement.options, (elemento) => {
        const divOption = document.createElement("div");
        divOption.className = "flex gap-2 p-1 cursor-pointer hover:bg-gray-100";
        const img = document.createElement("img");
        img.src = elemento.dataset.image ?? "";
        img.className = "w-8"
        
        divOption.appendChild(img);
        divOption.appendChild(document.createTextNode(elemento.text));
        divOption.addEventListener("click", (ev) => {
            comboContainerDisplay.innerHTML = `
                <img src="${elemento.dataset.image}" class="w-8"> 
                ${elemento.text}
            `;
            comboContainerOptions.classList.toggle('hidden');
            comboElement.value = elemento.value;
            comboElement.dispatchEvent(new Event('change'));
        });
        comboContainerOptions.appendChild(divOption);
    }) 
    comboContainer.appendChild(comboContainerOptions);

    comboContainerDisplay.addEventListener("click", () => {
        comboContainerOptions.classList.toggle('hidden')
    });  
}

export default comboImage;

Agora, basta integrar o código como um plugin do Alpine.js para ativar a funcionalidade no seu projeto.

import comboImage from './combo-image.js';
import Alpine from 'alpinejs';
window.Alpine = Alpine;

Alpine.directive('combo-image', comboImage);

Alpine.start();

Agora só precisamos carregar o plugin no elemento HTML select que desejamos ter o combobox com imagens.

<select name="pais" id="pais" x-data x-combo-image>
    <option value="BRA" data-image="imgs/BRA.png">Brasil</option>
    <option value="ARG" data-image="imgs/ARG.png">Argentina</option>
    <option value="URU" data-image="imgs/URU.png" selected>Uruguai</option>
</select>

Veja um exemplo online.

Bom esse é um exemplo simples que não deve ser muito dificil adaptar para a sua necessidade.