Select2 performance for large set of items

JqueryJquery Select2

Jquery Problem Overview


I'm using select2 jquery plugin with twitter bootstrap. It's working fine for smaller number of items. But when the list is huge (more than 1500 items) it really slows down. It's slowest in IE.

Normal Dropdownlist works very fast with more than 1500 items. Are there any workarounds for this situation?

Jquery Solutions


Solution 1 - Jquery

You can make this work good even in IE8 with paginating the suggestions,

Code:

// Function to shuffle the demo data
function shuffle(str) {
  return str
    .split('')
    .sort(function() {
      return 0.5 - Math.random();
  })
.join('');
}

// For demonstration purposes we first make
// a huge array of demo data (20 000 items)
// HEADS UP; for the _.map function i use underscore (actually lo-dash) here
function mockData() {
  return _.map(_.range(1, 20000), function(i) {
    return {
      id: i,
      text: shuffle('te ststr ing to shuffle') + ' ' + i,
    };
  });
}
(function() {
  // init select 2
  $('#test').select2({
    data: mockData(),
    placeholder: 'search',
    multiple: true,
    // query with pagination
    query: function(q) {
      var pageSize,
        results,
        that = this;
      pageSize = 20; // or whatever pagesize
      results = [];
      if (q.term && q.term !== '') {
        // HEADS UP; for the _.filter function i use underscore (actually lo-dash) here
        results = _.filter(that.data, function(e) {
          return e.text.toUpperCase().indexOf(q.term.toUpperCase()) >= 0;
        });
      } else if (q.term === '') {
        results = that.data;
      }
      q.callback({
        results: results.slice((q.page - 1) * pageSize, q.page * pageSize),
        more: results.length >= q.page * pageSize,
      });
    },
  });
})();

working example with 20000 items here: http://embed.plnkr.co/db8SXs/preview

plnkr embed does not support IE8 so try it out on IE8 with this link instead: http://run.plnkr.co/plunks/db8SXs/

Solution 2 - Jquery

I know it's an old question, but I wanted to share what worked for me. If you must pre-load the big list(depending on if you're starting from scratch or building on someone else's code, this may be easier), use the minimumInputLength as described here in the documentation. The huge list of options doesn't show until the user has typed a couple of characters. This greatly reduces the performance hit while rendering them when the Select2 dropdown is actually selected. Hope that helps!

Solution 3 - Jquery

Here's a working version for Select2 v4

Based on the answer here: and modified it to make searching work with lo-dash

$(function () {
    items = []
    for (var i = 0; i < 1000; i++) {
        items.push({ id: i, text : "item " + i})
    }

    pageSize = 50

    jQuery.fn.select2.amd.require(["select2/data/array", "select2/utils"],

    function (ArrayData, Utils) {
        function CustomData($element, options) {
            CustomData.__super__.constructor.call(this, $element, options);
        }
        Utils.Extend(CustomData, ArrayData);

        CustomData.prototype.query = function (params, callback) {
        
            var results = [];
            if (params.term && params.term !== '') {
              results = _.filter(items, function(e) {
                return e.text.toUpperCase().indexOf(params.term.toUpperCase()) >= 0;
              });
            } else {
              results = items;
            }

            if (!("page" in params)) {
                params.page = 1;
            }
            var data = {};
            data.results = results.slice((params.page - 1) * pageSize, params.page * pageSize);
            data.pagination = {};
            data.pagination.more = params.page * pageSize < results.length;
            callback(data);
        };

        $(document).ready(function () {
            $("select").select2({
                ajax: {},
                dataAdapter: CustomData
            });
        });
    })
});

JsFiddle: http://jsfiddle.net/nea053tw/

Edit: Fiddle changed.

Solution 4 - Jquery

The easiest and shortest works for me is:

$(".client_id").select2({
   minimumInputLength: 2
 });

You can change the value of minimumInputLength at your wish.

This way, select2 will not have to show whole list, rather it will bring result only after the fixed number of characters typed. Although you are still having the large array of list at front-end-code.

Also, if you are using allowClear, then you must declare placehodler like this:

$(".client_id").select2({
    minimumInputLength: 2,
    allowClear: true,
    placeholder: '--Select Client--'
 });

Check the documentation here http://select2.github.io/select2

If still your data is too large and still having performance issue, use Ajax method. It's better not to load too large data in select, istead use Ajax for Select2 https://select2.org/data-sources/ajax

Solution 5 - Jquery

So keep in mind you are loading >1500 actual elements onto the page in the form of <option>s, which can end up hurting page performance as well. As a user suggested in the a comment, you can solve the performance issue by making an AJAX call to a backend service that will return your values.

Select2 Ajax how-to

Solution 6 - Jquery

This is very old question and answer and even we have newer version of select2. but if someone is trying to search in optgroup too. can try this solution.

http://jsfiddle.net/na1zLkz3/4/

    // Function to shuffle the demo data 
var shuffle = function (str) {
    return str.split('').sort(function () {
      return 0.5 - Math.random();
    }).join('');
  };

// For demonstration purposes we first make
// a huge array of demo data (20 000 items)
// HEADS UP; for the _.map function i use underscore (actually lo-dash) here
var mockData = function () {
    var array = _.map(_.range(1, 10), function (i) {
        return {
          id  : i,
          text: shuffle('te ststr ing to shuffle') + ' ' + i
        };
      });
    return array;
  };
  var mockData1 = function () {
    var array = _.map(_.range(10, 15), function (i) {
        return {
          id  : i,
          text: shuffle('te ststr ing to shuffle') + ' ' + i
        };
      });
    return array;
  };
  var mockData2 = function () {
    var array = _.map(_.range(15, 20), function (i) {
        return {
          id  : i,
          text: shuffle('te ststr ing to shuffle') + ' ' + i
        };
      });
    return array;
  };
  // create demo data
  var dummyData = mockData();
  var dummyData1 = mockData1();
  var dummyData2 = mockData2();
  dummyData.push({
  text: 'canada',
  children: dummyData1
  });
  dummyData.push({
  text: 'USA',
  children: dummyData2
  });

  // init select 2
  $('#ddlCar').select2({
    data             : dummyData,
    // init selected from elements value
    initSelection    : function (element, callback) {
      var initialData = [];
      $(element.val().split(",")).each(function () {
        initialData.push({
          id  : this,
          text: this
        });
      });
      callback(initialData);
    },

    // NOT NEEDED: These are just css for the demo data
    dropdownCssClass : 'capitalize',
    containerCssClass: 'capitalize',

    // NOT NEEDED: text for loading more results
    formatLoadMore   : function() {return 'Loading more...'},
    
    // query with pagination
    query            : function (q) {
      var pageSize,
        results;
      pageSize = 20; // or whatever pagesize
      var results  = [];
      if (q.term && q.term !== "") {
        // HEADS UP; for the _.filter function i use underscore (actually lo-dash) here
        var results = this.data;
        var results = _.filter(results, function (e) {
        	if(typeof e.children != 'undefined')
          {
          	subresults = _.filter(e.children, function (f) {
            	return (f.text.toUpperCase().indexOf(q.term.toUpperCase()) >= 0);
            });
            if(subresults.length > 0)
            	return true;
            return false;
          }
          return (e.text.toUpperCase().indexOf(q.term.toUpperCase()) >= 0);
        });
        newresults = [];
        for (var i = 0, len = results.length; i < len; i++) {
        newresults[i] = {};
        if(typeof results[i].text != 'undefined')
        	newresults[i].text = results[i].text;
        if(typeof results[i].id != 'undefined')
        	newresults[i].id = results[i].id;
        if(typeof results[i].children != 'undefined')
        {
        	newresults[i].children = results[i].children;
        	newresults[i].children = _.filter(newresults[i].children, function (f) 							{
            	return (f.text.toUpperCase().indexOf(q.term.toUpperCase()) >= 0);
            });
        }
      }
      results = newresults;
      } else if (q.term === "") {
        results = this.data;
        
      }
      
      q.callback({
        results: results.slice((q.page - 1) * pageSize, q.page * pageSize),
        more   : results.length >= q.page * pageSize
      });
    }
  });

Solution 7 - Jquery

As yet another update, I wanted to share what worked for me, since finding support for something this old is increasingly difficult. I had installed select 2 with npm and found that there are two versions included. The default version require('select2') does not include support for the query argument. requiring the full version require('select2/dist/js/select2.full') and then using the code shown here worked for me with select 2 v4, with data() returning a list of 16k cities

       $(".select_2_cities").select2({
            theme: "bootstrap",
            data: data(),
            multiple: true,
            query            : function (q) {
              var pageSize,
                results;
              pageSize = 20; // or whatever pagesize
              var results  = [];
              if (q.term && q.term !== "") {
                // HEADS UP; for the _.filter function i use underscore (actually lo-dash) here
                var results = this.data;
                var results = _.filter(results, function (e) {
                    if(typeof e.children != 'undefined')
                  {
                    subresults = _.filter(e.children, function (f) {
                        return (f.text.toUpperCase().indexOf(q.term.toUpperCase()) >= 0);
                    });
                    if(subresults.length > 0)
                        return true;
                    return false;
                  }
                  return (e.text.toUpperCase().indexOf(q.term.toUpperCase()) >= 0);
                });
                newresults = [];
                for (var i = 0, len = results.length; i < len; i++) {
                newresults[i] = {};
                if(typeof results[i].text != 'undefined')
                    newresults[i].text = results[i].text;
                if(typeof results[i].id != 'undefined')
                    newresults[i].id = results[i].id;
                if(typeof results[i].children != 'undefined')
                {
                    newresults[i].children = results[i].children;
                    newresults[i].children = _.filter(newresults[i].children, function (f)                          {
                        return (f.text.toUpperCase().indexOf(q.term.toUpperCase()) >= 0);
                    });
                }
              }
              results = newresults;
              } else if (q.term === "") {
                results = this.data;
              }
              q.callback({
                results: results.slice((q.page - 1) * pageSize, q.page * pageSize),
                more   : results.length >= q.page * pageSize
              });
            }
        });

Solution 8 - Jquery

Unfortunatelly the answer is switch to selectize.js!

lofihelsinki solution works for <select>. Unfortunately it is not working with <select multiple>.

Because of this I switch from select2 to selectize.js. It runs really fast with large set of items.

Here is an example with 13500+ items! https://htmlcssjsp.vasilisp.repl.co/selectize-example/

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionManishView Question on Stackoverflow
Solution 1 - JqueryMarcusAsplundView Answer on Stackoverflow
Solution 2 - JqueryBammaHammaView Answer on Stackoverflow
Solution 3 - JquerylofihelsinkiView Answer on Stackoverflow
Solution 4 - JqueryJaber Al NahianView Answer on Stackoverflow
Solution 5 - JqueryKelzView Answer on Stackoverflow
Solution 6 - JqueryMaulik VoraView Answer on Stackoverflow
Solution 7 - JquerySeverianView Answer on Stackoverflow
Solution 8 - JqueryVasilis PlavosView Answer on Stackoverflow