Manish
Manish

Reputation: 1326

Select2 performance for large set of items

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?

Upvotes: 60

Views: 74346

Answers (8)

Kelz
Kelz

Reputation: 494

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 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

Upvotes: 8

Vasilis Plavos
Vasilis Plavos

Reputation: 583

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/

Upvotes: 1

Jaber Al Nahian
Jaber Al Nahian

Reputation: 1061

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

Upvotes: 9

bhamner
bhamner

Reputation: 447

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
              });
            }
        });

Upvotes: 0

lofihelsinki
lofihelsinki

Reputation: 2571

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.

Upvotes: 12

MarcusAsplund
MarcusAsplund

Reputation: 1284

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/

Upvotes: 53

Maulik Vora
Maulik Vora

Reputation: 2584

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
      });
    }
  });

Upvotes: 3

BammaHamma
BammaHamma

Reputation: 459

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!

Upvotes: 33

Related Questions