html – How to create a<select> with images in the options?

Question:

I thought I would know how to make a simple select in html with an image, but it doesn't work.

I'm starting to think it's a problem with modern browsers or HTML5.

CSS

select#gender option[value="Prima"] {
    background-image: url('../produtos/1.jpg');
}

select#gender option[value="Piana"] {
    background-image: url('../produtos/2.jpg');
}

select#gender option[value="Legno"] {
    background-image: url('../produtos/3.jpg');
    background-color: #cccccc;
}

HTML

<select>
    <option>Prima</option>
    <option>Piana</option>
    <option>Legno1</option>
    <option>Due</option>
    <option>Rustica</option>
    <option>Magna</option>
    <option>Flat</option>
</select>

I also tried directly on the option and nothing:

<option style="background-image: url('../produtos/2.jpg');">Prima</option>

How to put image in the select option?, with an icon to the left of the option.

Answer:

As already said, the option is an element that does not accept images. I developed a code in jQuery with CSS where you can replace the select with an HTML list that will simulate the select.

The code hides the select and creates in its place the HTML with the list simulating the options, where you can place an image beside the text.

It works like this:

Add the .fake-sel class .fake-sel :

<select class="fake-sel">
    <option>Prima</option>
    <option>Piana</option>
    <option>Legno1 foo foo</option>
</select>

JavaScript will identify the select by that class and build the HTML instead. The code I developed can be used in more than one select, just add the class in the selects you want to customize with images in the options.

You will see in the code an array like this:

var imgs_ = [
   [
      'https://www.cleverfiles.com/howto/wp-content/uploads/2016/08/mini.jpg',
      'https://oc2.ocstatic.com/images/logo_small.png',
      'https://upload.wikimedia.org/wikipedia/commons/3/3a/Cat03.jpg'
   ]
];

Note that this imgs_ array has another array where each position has a path to the images. You only need to place the path of the images relative to that option in each position. For example, if you are going to use more than one select, it would look like this:

<select class="fake-sel">
    <option>Prima</option>
    <option>Piana</option>
    <option>Legno1 foo foo</option>
</select>
<select class="fake-sel">
    <option>Opção 1</option>
    <option>Opção 2</option>
</select>

And the array like this:

var imgs_ = [
   [
      'https://www.cleverfiles.com/howto/wp-content/uploads/2016/08/mini.jpg',
      'https://oc2.ocstatic.com/images/logo_small.png',
      'https://upload.wikimedia.org/wikipedia/commons/3/3a/Cat03.jpg'
   ],
   [
      'https://www.cleverfiles.com/howto/wp-content/uploads/2016/08/mini.jpg',
      'https://oc2.ocstatic.com/images/logo_small.png'
   ]
];

Note that the array has two sub-arrays, where each one refers to a select in order, and the second select has two options, so the second array will have two items with the paths of the images.

In short, just add the .fake-sel class to the select you want to change and adjust the imgs_ array as explained above, and add the script below:

<script>
$(function(){

   var sels = $(".fake-sel");

   var imgs_ = [
      [
         'https://www.cleverfiles.com/howto/wp-content/uploads/2016/08/mini.jpg',
         'https://oc2.ocstatic.com/images/logo_small.png',
         'https://upload.wikimedia.org/wikipedia/commons/3/3a/Cat03.jpg'
      ]
   ];

   sels.each(function(x){

      var $t = $(this);

      var opts_ = '', first;

      $t.find("option").each(function(i){

         if(i == 0){
            first = "<li><img src='"+ imgs_[x][i] +"'>"+ $(this).text() +"</li>";
         }
         opts_ += "<li"+ (i == 0 ? " class='ativo'" : '') +"><img src='"+ imgs_[x][i] +"'>"+ $(this).text() +"</li>";
      });

      $t
      .wrap("<div class='fake-sel-wrap'></div>")
      .hide()
      .parent()
      .css("width", $t.outerWidth()+60)
      .append("<ul>"+ first+opts_ +"</ul>")
      .find("ul")
      .on("click", function(e){
         e.stopPropagation();
         $(this).toggleClass("ativo");
      })
      .find("li:not(:first)")
      .on("click", function(){
         $(this)
         .addClass("ativo")
         .siblings()
         .removeClass("ativo")
         .parent()
         .find("li:first")
         .html($(this).html());

         $t.val($(this).text());

      });
   });

   $(document).on("click", function(){
      $(".fake-sel-wrap ul").removeClass("ativo");
   });

});
</script>

Regarding the size of the images and other changes (colors etc.) I will explain further below. For now see the code working with just 1 select:

$(function(){
   
   var sels = $(".fake-sel");
   
   var imgs_ = [
      [
         'https://www.cleverfiles.com/howto/wp-content/uploads/2016/08/mini.jpg',
         'https://oc2.ocstatic.com/images/logo_small.png',
         'https://upload.wikimedia.org/wikipedia/commons/3/3a/Cat03.jpg'
      ]
   ];

   sels.each(function(x){
      
      var $t = $(this);
      
      var opts_ = '', first;
      
      $t.find("option").each(function(i){
         
         if(i == 0){
            first = "<li><img src='"+ imgs_[x][i] +"'>"+ $(this).text() +"</li>";
         }
         opts_ += "<li"+ (i == 0 ? " class='ativo'" : '') +"><img src='"+ imgs_[x][i] +"'>"+ $(this).text() +"</li>";
      });

      $t
      .wrap("<div class='fake-sel-wrap'></div>")
      .parent()
      .css("width", $t.outerWidth()+60)
      .append("<ul>"+ first+opts_ +"</ul>")
      .find("ul")
      .on("click", function(e){
         e.stopPropagation();
         $(".fake-sel-wrap ul")
         .not(this)
         .removeClass("ativo");
         $(this).toggleClass("ativo");
      })
      .find("li:not(:first)")
      .on("click", function(){
         $(this)
         .addClass("ativo")
         .siblings()
         .removeClass("ativo")
         .parent()
         .find("li:first")
         .html($(this).html());
         
         $t.val($(this).text());
         
      });
   });
   
   $(document).on("click", function(){
      $(".fake-sel-wrap ul").removeClass("ativo");
   });
   
});
.fake-sel{
   display: none;
}

.fake-sel-wrap{
   display: inline-block;
   position: relative;
   height: 46px;
}

.fake-sel-wrap ul{
   margin: 0;
   padding: 0;
   list-style: none;
   border: 1px solid #ddd;
   position: absolute;
   top: 0;
   left: 0;
   font-family: Arial;
   font-size: 14px;
   width: 100%;
   height: 100%;
   overflow: hidden;
   cursor: default;
   background-color: white;
}

.fake-sel-wrap ul li{
   padding: 3px;
   line-height: 1em;
   display: flex;
   align-items: center;
}


.fake-sel-wrap ul li:nth-child(1){
   border-bottom: 1px solid #ddd;
}

.fake-sel-wrap ul li.ativo{
   background-color: blue;
   color: white;
}

.fake-sel-wrap ul li:not(:nth-child(1)):not(.ativo):hover{
   background-color: #ddd;
}


.fake-sel-wrap ul.ativo{
   overflow: auto;
   height: auto;
}

.fake-sel-wrap ul li img{
   width: 40px;
   height: 40px;
   margin-right: 10px;
}

/* ESTE É O CSS DA SETINHA */
.fake-sel-wrap ul li:nth-child(1)::after{
   content: '';
   width: 0;
   height: 0;
   border-style: solid;
   border-width: 6px 5px 0 5px;
   border-color: #000000 transparent transparent transparent;
   margin-left: auto;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<select class="fake-sel">
    <option>Prima</option>
    <option>Piana</option>
    <option>Legno1 foo foo</option>
</select>

When you click on an option, the hidden select is also changed according to the option clicked, so you can send it through the form with ease, just put a name :

<select class="fake-sel" name="coloque um nome">
    <option>Prima</option>
    <option>Piana</option>
    <option>Legno1 foo foo</option>
</select>

Regarding the size of the images, see that I put a square size of 40×40 pixels. You can use whatever size you want, just make some adjustments to the CSS and a part of the script.

In CSS:

.fake-sel-wrap{
   display: inline-block;
   position: relative;
   height: 46px;
}

Where has height: 46px; above is the sum of the image height +6 pixels referring to the 3px padding above and below the <li> . If you are going to use an image that is 20px tall, for example, change the 46px to 26px .

Here the dimensions of the image are defined:

.fake-sel-wrap ul li img{
   width: 40px;
   height: 40px;
   margin-right: 10px;
}

If you want another size, just change the width and height (don't forget to adjust the height of the .fake-sel-wrap as explained above).

At JS:

In the script, you will only adjust a value according to the image size you set in CSS, in this part:

.css("width", $t.outerWidth()+60)

Where it has 60 , it refers to the width of the image +20 pixels, that is, if you are going to use a 20px image, just change the 60 to 40 .

See an example now with 2 selects:

$(function(){
   
   var sels = $(".fake-sel");
   
   var imgs_ = [
      [
         'https://www.cleverfiles.com/howto/wp-content/uploads/2016/08/mini.jpg',
         'https://oc2.ocstatic.com/images/logo_small.png',
         'https://upload.wikimedia.org/wikipedia/commons/3/3a/Cat03.jpg'
      ],
      [
         'https://oc2.ocstatic.com/images/logo_small.png',
         'https://upload.wikimedia.org/wikipedia/commons/3/3a/Cat03.jpg'
      ]
   ];

   sels.each(function(x){
      
      var $t = $(this);
      
      var opts_ = '', first;
      
      $t.find("option").each(function(i){
         
         if(i == 0){
            first = "<li><img src='"+ imgs_[x][i] +"'>"+ $(this).text() +"</li>";
         }
         opts_ += "<li"+ (i == 0 ? " class='ativo'" : '') +"><img src='"+ imgs_[x][i] +"'>"+ $(this).text() +"</li>";
      });

      $t
      .wrap("<div class='fake-sel-wrap'></div>")
      .parent()
      .css("width", $t.outerWidth()+60)
      .append("<ul>"+ first+opts_ +"</ul>")
      .find("ul")
      .on("click", function(e){
         e.stopPropagation();
         $(".fake-sel-wrap ul")
         .not(this)
         .removeClass("ativo");
         $(this).toggleClass("ativo");
      })
      .find("li:not(:first)")
      .on("click", function(){
         $(this)
         .addClass("ativo")
         .siblings()
         .removeClass("ativo")
         .parent()
         .find("li:first")
         .html($(this).html());
         
         $t.val($(this).text());
         
      });
   });
   
   $(document).on("click", function(){
      $(".fake-sel-wrap ul").removeClass("ativo");
   });
   
});
.fake-sel{
   display: none;
}

.fake-sel-wrap{
   display: inline-block;
   position: relative;
   height: 46px;
}

.fake-sel-wrap ul{
   margin: 0;
   padding: 0;
   list-style: none;
   border: 1px solid #ddd;
   position: absolute;
   top: 0;
   left: 0;
   font-family: Arial;
   font-size: 14px;
   width: 100%;
   height: 100%;
   overflow: hidden;
   cursor: default;
   background-color: white;
}

.fake-sel-wrap ul li{
   padding: 3px;
   line-height: 1em;
   display: flex;
   align-items: center;
}


.fake-sel-wrap ul li:nth-child(1){
   border-bottom: 1px solid #ddd;
}

.fake-sel-wrap ul li.ativo{
   background-color: blue;
   color: white;
}

.fake-sel-wrap ul li:not(:nth-child(1)):not(.ativo):hover{
   background-color: #ddd;
}


.fake-sel-wrap ul.ativo{
   overflow: auto;
   height: auto;
}

.fake-sel-wrap ul li img{
   width: 40px;
   height: 40px;
   margin-right: 10px;
}

/* ESTE É O CSS DA SETINHA */
.fake-sel-wrap ul li:nth-child(1)::after{
   content: '';
   width: 0;
   height: 0;
   border-style: solid;
   border-width: 6px 5px 0 5px;
   border-color: #000000 transparent transparent transparent;
   margin-left: auto;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<select class="fake-sel">
    <option>Prima</option>
    <option>Piana</option>
    <option>Legno1 foo foo</option>
</select>
<select class="fake-sel">
    <option>Piana</option>
    <option>Legno1 foo foo</option>
</select>
Scroll to Top