How to render html in select2 options
Asked Answered
S

8

52

In this example of data loaded from a remote source I can see images and other html elements rendered as options. I'd like to accomplish the same thing using data in a local array. I've tried building an array as described in the documentation and adding it with the data option but the html is rendered as plaintext:

var data = [
  { id: 0, text: '<div style="color:green">enhancement</div>' },
  { id: 1, text: '<div style="color:red">bug</div><div><small>This is some small text on a new line</small></div>' }];

$("select").select2({
  data: data
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.2/css/select2.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.2/js/select2.min.js"></script>

<select></select>

How can I add html content to the select2 options?

Seagraves answered 1/4, 2016 at 15:59 Comment(0)
S
71

Ok, played with this for a while and found a working solution so I'll answer my own question here.

The key here for me is to build a data array with content for both templateSelection and templateResult. The latter renders fine in the dropdown but any multiline content will not be contained in the select2 element so needs to be displayed inline (or at least on a single line). Defining escapeMarkup as an option allows overriding of the core function which would normally strip out html content.

It's also important to define the title attribute since otherwise you'll end up with html tags in the tooltip.

var data = [{
  id: 0,
  text: '<div style="color:green">enhancement</div>',
  html: '<div style="color:green">enhancement</div><div><b>Select2</b> supports custom themes using the theme option so you can style Select2 to match the rest of your application.</div>',
  title: 'enchancement'
}, {
  id: 1,
  text: '<div style="color:red">bug</div>',
  html: '<div style="color:red">bug</div><div><small>This is some small text on a new line</small></div>',
  title: 'bug'
}];

$("select").select2({
  data: data,
  escapeMarkup: function(markup) {
    return markup;
  },
  templateResult: function(data) {
    return data.html;
  },
  templateSelection: function(data) {
    return data.text;
  }
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.2/css/select2.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.2/js/select2.min.js"></script>

<select></select>

Alternately, with a couple small CSS tweaks you can allow the full html option content to display inside of the select container without the need for the template callbacks:

var data = [{
  id: 0,
  text: '<div style="font-size: 1.2em; color:green">enhancement</div><div><b>Select2</b> supports custom themes using the theme option so you can style Select2 to match the rest of your application.</div>',
  title: 'enchancement'
}, {
  id: 1,
  text: '<div style="color:red">bug</div><div><small>This is some small text on a new line</small></div>',
  title: 'bug'
}];

$("select").select2({
  data: data,
  escapeMarkup: function(markup) {
    return markup;
  }
})
.select2-container .select2-selection--single {
  height: auto!important;
  padding: 5px 0;
}
.select2-container--default .select2-selection--single .select2-selection__rendered {
  line-height: normal!important;
}
.select2-container .select2-selection--single .select2-selection__rendered {
  white-space: normal!important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.2/css/select2.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.2/js/select2.min.js"></script>

<select style="width: 100%"></select>
Seagraves answered 1/4, 2016 at 16:34 Comment(4)
Great answer Billy!Put
Method works like charm. However if you do not wrap everything inside $(function(){}); Select2 will populate your data but with no HTML. Good thing to point out. Hope this will help someone.Crosslink
@WaiylKarim - it probably depends on the order of things. This works fine for me as long as my javascript comes after the markup.Seagraves
@billynoah agreedCrosslink
V
35

If I am not mistaken you can only render HTML if you set the templateResult and templateSelection options and have them return a jQuery object.

var data = [
      { id: 0, text: '<div style="color:green">enhancement</div>' },
      { id: 1, text: '<div style="color:red">bug</div><div><small>This is some small text on a new line</small></div>' }];

    $("select").select2({
      data: data,
      templateResult: function (d) { return $(d.text); },
      templateSelection: function (d) { return $(d.text); },
      
    })
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.2/css/select2.min.css" rel="stylesheet" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.2/js/select2.min.js"></script>

    <select></select>
 
Violoncellist answered 1/4, 2016 at 16:5 Comment(1)
Thanks - actually just got that bit as well. This is the right direction but as you can see, full html content does not render well once selected, so two different formats are needed. Nonetheless this has the vital building blocks to get the job done so I'll give it an upvote ;-)Seagraves
S
17

Simply you can add another field with the html to your data array and make your own template using the templateResult option like so

JSFiddle Demo

var data = [{
   id: 0,
   text: 'enhancement',
   html: '<div style="color:green">enhancement</div>'
}, {
   id: 1,
   text: 'bug',
   html: '<div style="color:red">bug</div><div><small>This is some small text on a new line</small></div>'
}];

function template(data) {
    return data.html;
}

$("select").select2({
   data: data,
   templateResult: template,
   escapeMarkup: function(m) {
      return m;
   }
});
Selfrespect answered 12/8, 2017 at 4:55 Comment(0)
K
8

Another example is defined like so

function template(data) {
    if ($(data.html).length === 0) {
        return data.text;
    }
    return $(data.html);
}

$("select").select2({
   ajax: {
        url: 'routeurl',
        dataType: 'json',
        type: 'POST',
        processResults: function(data) {
            return {
                results: data
            };
        },
        cache: true
    },
    allowClear: true,
    placeholder: 'Select at least one element',
    templateResult: template,
    templateSelection: template
});

The Endpoint result with format json as so

[{
   id: 0,
   text: 'enhancement',
   html: '<div style="color:green">enhancement</div>'
}, {
   id: 1,
   text: 'bug',
   html: '<div style="color:red">bug</div><div><small>This is some small text on a new line</small></div>'
}, {
   id: 2,
   text: 'success',
   html: 'Success'
}]
Kickstand answered 2/5, 2018 at 18:58 Comment(0)
T
1

Using the

escapeMarkup: function(m) { return m; }

opens the XSS vulnerability (https://codepen.io/nkorovikov/pen/ZEBdMBP)

I didn't find a way to use templates for data from an array, but templates perfectly work for data from ajax and when adding select elements directly to HTML

<select class="templatingSelect2">
        <option value=""></option>
        <option value="usd">USD</option>
        <option value="euro">Euro</option>
        <option value="gbp">Pound</option>
        </select>

(https://codepen.io/SitePoint/pen/bELxVw)

Tindle answered 19/3, 2021 at 9:9 Comment(0)
N
1

An addendum to the answer posted by @EatenByAGrue

If you need to search both the text (enhancement and bug, in the provided example) AND the html contents as well (for example, This is some small text), you will need to expand on the matching logic, as shown in the official documentation.

For example:

var data = [{
  id: 0,
  text: '<div style="color:green">enhancement</div>',
  html: '<div style="color:green">enhancement</div><div><b>Select2</b> supports custom themes using the theme option so you can style Select2 to match the rest of your application.</div>',
  title: 'enchancement'
}, {
  id: 1,
  text: '<div style="color:red">bug</div>',
  html: '<div style="color:red">bug</div><div><small>This is some small text on a new line</small></div>',
  title: 'bug'
}];

$("select").select2({
  data: data,
  escapeMarkup: function(markup) {
    return markup;
  },
  templateResult: function(data) {
    return data.html;
  },
  templateSelection: function(data) {
    return data.text;
  },
  matcher: matcher
});

function matcher(params, data) {
    // If there are no search terms, return all of the data
    if ($.trim(params.term) === '') {
        return data;
    }

    // Do not display the item if there is no 'text' property
    if (typeof data.text === 'undefined') {
        return null;
    }

    // `params.term` should be the term that is used for searching
    // `data.text` is the text that is displayed for the data object
    // `data.html` is the additional / info text, within the option, seen once the dropdown is shown
    // The search is case insensitive
    if (data.text.toLowerCase().indexOf(params.term.toLowerCase()) > -1 || (typeof data.html !== 'undefined' && data.html.toLowerCase().indexOf(params.term.toLowerCase()) > -1)) {
        var modifiedData = $.extend({}, data, true);
        modifiedData.text += ' (matched)';

        // You can return modified objects from here
        // This includes matching the `children` how you want in nested data sets
        return modifiedData;
    }

    // Return `null` if the term should not be displayed
    return null;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.8/css/select2.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.8/js/select2.min.js"></script>

<select></select>
Nonsectarian answered 6/12, 2022 at 17:0 Comment(1)
Good idea. Searching all the components of an option could be really useful in some cases!Seagraves
R
-1

Change the text property to HTML in select2 control:

$(document).ready(function() {

  function select2OptionFormat(option) {
    var originalOption = option.element;
      if ($(originalOption).data('html')) {
        return $(originalOption).data('html');
      }          
      return option.text;
  }


  $('select').select2({
    formatResult: select2OptionFormat,
    formatSelection: select2OptionFormat,
    escapeMarkup: function(m) { return m; }
  });


});

Reference : https://codepen.io/kohnmd/pen/KwYvvM

Ritch answered 31/3, 2020 at 8:27 Comment(0)
R
-2

Here's an alternative option using jQuery

$('.select2').on("select2:open", function(e) {
        setTimeout(function(){
            $('#select2-myselect2name-results li').after('my html code here');
        }, 500);
 });
Robin answered 13/3, 2021 at 22:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.