Reputation: 1326
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
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.
Upvotes: 8
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
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
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
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
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
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
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