1
0
mirror of synced 2024-11-26 23:06:02 +03:00

Improve data array speeds

This improves the speeds of creating the `<option>` tags for array
data. By creating the HTML ahead of time and putting it into an
`<option>` element before passing it to jQuery, it cuts down on the
time it takes for jQuery to process the HTML string and convert it
into DOM elements. This proved to be incredibly slow.

This also changes the `item` function to return early when the
data exists. The `$e.data` call has also been switched to a
`$.data` call, which doesn't need to check the DOM to see if the
data attributes already exist. This cuts down on a bit of wasted
time, as the `data` key should never be present in the DOM, and it
should be ignored if it is.
This commit is contained in:
Kevin Brown 2014-11-07 11:13:53 -05:00
parent 56dbbf8cdb
commit 05ddbec1a7
8 changed files with 334 additions and 228 deletions

View File

@ -1098,68 +1098,87 @@ define('select2/data/select',[
}; };
SelectAdapter.prototype.option = function (data) { SelectAdapter.prototype.option = function (data) {
var $option = $('<option></option>'); var option = document.createElement('option');
$option.text(data.text); option.value = data.id;
$option.val(data.id);
$option.prop('disabled', data.disabled || false);
$option.prop('selected', data.selected || false);
// Get any automatically generated data values if (data.disabled) {
var detectedData = this.item($option); option.disabled = true;
}
// Merge it with the already present data if (data.selected) {
var combinedData = $.extend({}, data, detectedData); option.selected = true;
}
option.innerText = data.text;
var $option = $(option);
var normalizedData = this._normalizeItem(data);
// Override the option's data with the combined data // Override the option's data with the combined data
$option.data('data', combinedData); $.data($option, normalizedData);
return $option; return $option;
}; };
SelectAdapter.prototype.item = function ($option) { SelectAdapter.prototype.item = function ($option) {
var data = $option.data('data'); var data = {};
// If the data has already be generated, use it if ($.hasData($option)) {
if (data == null) { data = $option.data('data');
if ($option.is('option')) {
data = {
id: $option.val(),
text: $option.html(),
disabled: $option.prop('disabled')
};
} else if ($option.is('optgroup')) {
data = {
text: $option.attr('label'),
children: []
};
var $children = $option.children('option'); if (data != null) {
var children = []; return data;
for (var c = 0; c < $children.length; c++) {
var $child = $($children[c]);
var child = this.item($child);
children.push(child);
}
data.children = children;
} }
if (data.id && this.container != null) {
data._resultId = this.generateResultId(this.container, data);
}
data.selected = $option.prop('selected');
$option.data('data', data);
} }
if ($option.is('option')) {
data = {
id: $option.val(),
text: $option.html(),
disabled: $option.prop('disabled')
};
} else if ($option.is('optgroup')) {
data = {
text: $option.attr('label'),
children: []
};
var $children = $option.children('option');
var children = [];
for (var c = 0; c < $children.length; c++) {
var $child = $($children[c]);
var child = this.item($child);
children.push(child);
}
data.children = children;
}
data = this._normalizeItem(data);
$option.data('data', data);
return data; return data;
}; };
SelectAdapter.prototype._normalizeItem = function (item) {
var defaults = {
selected: false,
disabled: false
};
if (item._resultId == null && item.id && this.container != null) {
item._resultId = this.generateResultId(this.container, item);
}
return $.extend({}, defaults, item);
};
SelectAdapter.prototype.matches = function (params, data) { SelectAdapter.prototype.matches = function (params, data) {
var matcher = this.options.get('matcher'); var matcher = this.options.get('matcher');

107
dist/js/select2.amd.js vendored
View File

@ -1098,68 +1098,87 @@ define('select2/data/select',[
}; };
SelectAdapter.prototype.option = function (data) { SelectAdapter.prototype.option = function (data) {
var $option = $('<option></option>'); var option = document.createElement('option');
$option.text(data.text); option.value = data.id;
$option.val(data.id);
$option.prop('disabled', data.disabled || false);
$option.prop('selected', data.selected || false);
// Get any automatically generated data values if (data.disabled) {
var detectedData = this.item($option); option.disabled = true;
}
// Merge it with the already present data if (data.selected) {
var combinedData = $.extend({}, data, detectedData); option.selected = true;
}
option.innerText = data.text;
var $option = $(option);
var normalizedData = this._normalizeItem(data);
// Override the option's data with the combined data // Override the option's data with the combined data
$option.data('data', combinedData); $.data($option, normalizedData);
return $option; return $option;
}; };
SelectAdapter.prototype.item = function ($option) { SelectAdapter.prototype.item = function ($option) {
var data = $option.data('data'); var data = {};
// If the data has already be generated, use it if ($.hasData($option)) {
if (data == null) { data = $option.data('data');
if ($option.is('option')) {
data = {
id: $option.val(),
text: $option.html(),
disabled: $option.prop('disabled')
};
} else if ($option.is('optgroup')) {
data = {
text: $option.attr('label'),
children: []
};
var $children = $option.children('option'); if (data != null) {
var children = []; return data;
for (var c = 0; c < $children.length; c++) {
var $child = $($children[c]);
var child = this.item($child);
children.push(child);
}
data.children = children;
} }
if (data.id && this.container != null) {
data._resultId = this.generateResultId(this.container, data);
}
data.selected = $option.prop('selected');
$option.data('data', data);
} }
if ($option.is('option')) {
data = {
id: $option.val(),
text: $option.html(),
disabled: $option.prop('disabled')
};
} else if ($option.is('optgroup')) {
data = {
text: $option.attr('label'),
children: []
};
var $children = $option.children('option');
var children = [];
for (var c = 0; c < $children.length; c++) {
var $child = $($children[c]);
var child = this.item($child);
children.push(child);
}
data.children = children;
}
data = this._normalizeItem(data);
$option.data('data', data);
return data; return data;
}; };
SelectAdapter.prototype._normalizeItem = function (item) {
var defaults = {
selected: false,
disabled: false
};
if (item._resultId == null && item.id && this.container != null) {
item._resultId = this.generateResultId(this.container, item);
}
return $.extend({}, defaults, item);
};
SelectAdapter.prototype.matches = function (params, data) { SelectAdapter.prototype.matches = function (params, data) {
var matcher = this.options.get('matcher'); var matcher = this.options.get('matcher');

View File

@ -10633,68 +10633,87 @@ define('select2/data/select',[
}; };
SelectAdapter.prototype.option = function (data) { SelectAdapter.prototype.option = function (data) {
var $option = $('<option></option>'); var option = document.createElement('option');
$option.text(data.text); option.value = data.id;
$option.val(data.id);
$option.prop('disabled', data.disabled || false);
$option.prop('selected', data.selected || false);
// Get any automatically generated data values if (data.disabled) {
var detectedData = this.item($option); option.disabled = true;
}
// Merge it with the already present data if (data.selected) {
var combinedData = $.extend({}, data, detectedData); option.selected = true;
}
option.innerText = data.text;
var $option = $(option);
var normalizedData = this._normalizeItem(data);
// Override the option's data with the combined data // Override the option's data with the combined data
$option.data('data', combinedData); $.data($option, normalizedData);
return $option; return $option;
}; };
SelectAdapter.prototype.item = function ($option) { SelectAdapter.prototype.item = function ($option) {
var data = $option.data('data'); var data = {};
// If the data has already be generated, use it if ($.hasData($option)) {
if (data == null) { data = $option.data('data');
if ($option.is('option')) {
data = {
id: $option.val(),
text: $option.html(),
disabled: $option.prop('disabled')
};
} else if ($option.is('optgroup')) {
data = {
text: $option.attr('label'),
children: []
};
var $children = $option.children('option'); if (data != null) {
var children = []; return data;
for (var c = 0; c < $children.length; c++) {
var $child = $($children[c]);
var child = this.item($child);
children.push(child);
}
data.children = children;
} }
if (data.id && this.container != null) {
data._resultId = this.generateResultId(this.container, data);
}
data.selected = $option.prop('selected');
$option.data('data', data);
} }
if ($option.is('option')) {
data = {
id: $option.val(),
text: $option.html(),
disabled: $option.prop('disabled')
};
} else if ($option.is('optgroup')) {
data = {
text: $option.attr('label'),
children: []
};
var $children = $option.children('option');
var children = [];
for (var c = 0; c < $children.length; c++) {
var $child = $($children[c]);
var child = this.item($child);
children.push(child);
}
data.children = children;
}
data = this._normalizeItem(data);
$option.data('data', data);
return data; return data;
}; };
SelectAdapter.prototype._normalizeItem = function (item) {
var defaults = {
selected: false,
disabled: false
};
if (item._resultId == null && item.id && this.container != null) {
item._resultId = this.generateResultId(this.container, item);
}
return $.extend({}, defaults, item);
};
SelectAdapter.prototype.matches = function (params, data) { SelectAdapter.prototype.matches = function (params, data) {
var matcher = this.options.get('matcher'); var matcher = this.options.get('matcher');

File diff suppressed because one or more lines are too long

107
dist/js/select2.js vendored
View File

@ -1526,68 +1526,87 @@ define('select2/data/select',[
}; };
SelectAdapter.prototype.option = function (data) { SelectAdapter.prototype.option = function (data) {
var $option = $('<option></option>'); var option = document.createElement('option');
$option.text(data.text); option.value = data.id;
$option.val(data.id);
$option.prop('disabled', data.disabled || false);
$option.prop('selected', data.selected || false);
// Get any automatically generated data values if (data.disabled) {
var detectedData = this.item($option); option.disabled = true;
}
// Merge it with the already present data if (data.selected) {
var combinedData = $.extend({}, data, detectedData); option.selected = true;
}
option.innerText = data.text;
var $option = $(option);
var normalizedData = this._normalizeItem(data);
// Override the option's data with the combined data // Override the option's data with the combined data
$option.data('data', combinedData); $.data($option, normalizedData);
return $option; return $option;
}; };
SelectAdapter.prototype.item = function ($option) { SelectAdapter.prototype.item = function ($option) {
var data = $option.data('data'); var data = {};
// If the data has already be generated, use it if ($.hasData($option)) {
if (data == null) { data = $option.data('data');
if ($option.is('option')) {
data = {
id: $option.val(),
text: $option.html(),
disabled: $option.prop('disabled')
};
} else if ($option.is('optgroup')) {
data = {
text: $option.attr('label'),
children: []
};
var $children = $option.children('option'); if (data != null) {
var children = []; return data;
for (var c = 0; c < $children.length; c++) {
var $child = $($children[c]);
var child = this.item($child);
children.push(child);
}
data.children = children;
} }
if (data.id && this.container != null) {
data._resultId = this.generateResultId(this.container, data);
}
data.selected = $option.prop('selected');
$option.data('data', data);
} }
if ($option.is('option')) {
data = {
id: $option.val(),
text: $option.html(),
disabled: $option.prop('disabled')
};
} else if ($option.is('optgroup')) {
data = {
text: $option.attr('label'),
children: []
};
var $children = $option.children('option');
var children = [];
for (var c = 0; c < $children.length; c++) {
var $child = $($children[c]);
var child = this.item($child);
children.push(child);
}
data.children = children;
}
data = this._normalizeItem(data);
$option.data('data', data);
return data; return data;
}; };
SelectAdapter.prototype._normalizeItem = function (item) {
var defaults = {
selected: false,
disabled: false
};
if (item._resultId == null && item.id && this.container != null) {
item._resultId = this.generateResultId(this.container, item);
}
return $.extend({}, defaults, item);
};
SelectAdapter.prototype.matches = function (params, data) { SelectAdapter.prototype.matches = function (params, data) {
var matcher = this.options.get('matcher'); var matcher = this.options.get('matcher');

File diff suppressed because one or more lines are too long

View File

@ -121,68 +121,87 @@ define([
}; };
SelectAdapter.prototype.option = function (data) { SelectAdapter.prototype.option = function (data) {
var $option = $('<option></option>'); var option = document.createElement('option');
$option.text(data.text); option.value = data.id;
$option.val(data.id);
$option.prop('disabled', data.disabled || false);
$option.prop('selected', data.selected || false);
// Get any automatically generated data values if (data.disabled) {
var detectedData = this.item($option); option.disabled = true;
}
// Merge it with the already present data if (data.selected) {
var combinedData = $.extend({}, data, detectedData); option.selected = true;
}
option.innerText = data.text;
var $option = $(option);
var normalizedData = this._normalizeItem(data);
// Override the option's data with the combined data // Override the option's data with the combined data
$option.data('data', combinedData); $.data($option, normalizedData);
return $option; return $option;
}; };
SelectAdapter.prototype.item = function ($option) { SelectAdapter.prototype.item = function ($option) {
var data = $option.data('data'); var data = {};
// If the data has already be generated, use it if ($.hasData($option)) {
if (data == null) { data = $option.data('data');
if ($option.is('option')) {
data = {
id: $option.val(),
text: $option.html(),
disabled: $option.prop('disabled')
};
} else if ($option.is('optgroup')) {
data = {
text: $option.attr('label'),
children: []
};
var $children = $option.children('option'); if (data != null) {
var children = []; return data;
for (var c = 0; c < $children.length; c++) {
var $child = $($children[c]);
var child = this.item($child);
children.push(child);
}
data.children = children;
} }
if (data.id && this.container != null) {
data._resultId = this.generateResultId(this.container, data);
}
data.selected = $option.prop('selected');
$option.data('data', data);
} }
if ($option.is('option')) {
data = {
id: $option.val(),
text: $option.html(),
disabled: $option.prop('disabled')
};
} else if ($option.is('optgroup')) {
data = {
text: $option.attr('label'),
children: []
};
var $children = $option.children('option');
var children = [];
for (var c = 0; c < $children.length; c++) {
var $child = $($children[c]);
var child = this.item($child);
children.push(child);
}
data.children = children;
}
data = this._normalizeItem(data);
$option.data('data', data);
return data; return data;
}; };
SelectAdapter.prototype._normalizeItem = function (item) {
var defaults = {
selected: false,
disabled: false
};
if (item._resultId == null && item.id && this.container != null) {
item._resultId = this.generateResultId(this.container, item);
}
return $.extend({}, defaults, item);
};
SelectAdapter.prototype.matches = function (params, data) { SelectAdapter.prototype.matches = function (params, data) {
var matcher = this.options.get('matcher'); var matcher = this.options.get('matcher');

View File

@ -238,13 +238,24 @@ test('empty optgroups are still shown when queried', function (assert) {
var data = new SelectData($select, options); var data = new SelectData($select, options);
data.query({}, function (data) { data.query({}, function (data) {
assert.deepEqual( assert.equal(
data[1], data.length,
{ 2,
text: 'Empty',
children: []
},
'The empty optgroup element should still be returned when queried' 'The empty optgroup element should still be returned when queried'
); );
var item = data[1];
assert.equal(
item.text,
'Empty',
'The text of the empty optgroup should match the label'
);
assert.equal(
item.children.length,
0,
'There should be no children in the empty opgroup'
);
}); });
}); });