Reputation: 3758
I have a html table which I'm sorting with javascript. The table can easily have more than a +1000 rows.
This is what I currently have and the html looks similar to this:
<div id="mycollection" class="table">
<div class="title">
<div class="A">id</div>
<div class="B">name</div>
<div class="C">total</div>
<div class="D">cost</div>
</div>
<div class="row">
<div class="A">1</div>
<div class="B">Jack</div>
<div class="C">101</div>
<div class="D">$5</div>
</div>
<div class="row">
<div class="A">5</div>
<div class="B">Zoe</div>
<div class="C">5</div>
<div class="D">$10</div>
</div>
<div class="row">
<div class="A">3</div>
<div class="B">Adam</div>
<div class="C">21</div>
<div class="D">$7</div>
</div>
</div>
My javascript looks like this:
function testSort(s) {
var column = s;
var parent = document.getElementById('mycollection');
var rows = parent.children; // NodeList
rows = Array.prototype.slice.call(rows, 0); // change NodeList to array
var top = rows.shift(); // remove title row
// sort
rows.sort(function(a, b) {
if(a.getElementsByClassName(column)[0].textContent < b.getElementsByClassName(column)[0].textContent) return -1;
if(a.getElementsByClassName(column)[0].textContent > b.getElementsByClassName(column)[0].textContent) return 1;
return 0;
});
// recreate div
parent.innerHTML = ''; // clear
parent.appendChild(top); // add title row
for(var i=0, l=rows.length; i<l; i++) { // add rows
parent.appendChild(rows[i]);
}
}
testSort('D');
I've set up a jsfiddle here:
http://jsfiddle.net/rotaercz/f6p377au/
*Additionally is there a way to make the C and D column sort correctly? It's currently sorting only alphabetically.
Upvotes: 0
Views: 81
Reputation: 413737
As a general rule, calling out to DOM access or (!!) manipulation facilities from inside a .sort()
comparator function is something to avoid with all possible effort. In this case, you should make a single pass through the nodes and extract the text content once. That will make your sort much, much faster, particularly when there are more than a few rows.
Here's an example of how to do this:
function testSort(s) {
var column = s;
var parent = document.getElementById('mycollection');
var rows = parent.children; // NodeList
rows = Array.prototype.slice.call(rows, 0); // change NodeList to array
var top = rows.shift(); // remove title row
var extracted = rows.map(function(row) {
return {
text: row.getElementsByClassName(column)[0].textContent,
row: row
};
});
// sort
extracted.sort(function(a, b) {
if(a.text < b.text) return -1;
if(a.text > b.text) return 1;
return 0;
});
// recreate div
parent.innerHTML = ''; // clear
parent.appendChild(top); // add title row
for(var i=0, l=extracted.length; i<l; i++) { // add rows
parent.appendChild(extracted[i].row);
}
}
Before sorting, that code transforms the rows
array into another array, one consisting of plain objects. Each object has the text extracted from the column and a reference back to the DOM node.
The sort function therefore can refer directly to the string without having to call .getElementsByClassName()
.
If you have 100 rows, that will probably save several hundred calls to .getElementsByClassName
; for 1000 rows, it'll save several thousand.
As to sorting by the numeric value of content, you'll have to either detect that the text looks numeric (by whatever criteria you like) and implicitly convert it, or else allow for an extra parameter to be passed to indicate that should be done. Personally I would go the second route, and change the function signature:
function testSort(s, numeric) {
Then the extraction array would be built similarly, but now it would convert to numeric when told to do so:
var extracted = rows.map(function(row) {
var sv = row.getElementsByClassName(column)[0].textContent;
return {
text: numeric ? +sv.replace(/\D/g, '') : sv,
row: row
};
});
Now the comparisons in the sort comparator function will work properly when you pass in the numeric flag:
testSort("D", true);
Link to the fixed version of your original jsfiddle.
Upvotes: 3
Reputation: 3664
A common problem when dealing with large number of elements is the continuous redrawing of the DOM. To minimise this and make it faster you can clone the parts of the DOM you wish to operate on, do your changes and then do a replace or a you can create a string representing your elements and append the end string ONLY once.
Example:
var urlsDOM = '';
resultSet.forEach(function(result){
urlsDOM += '<li>' + result.someProperty + '</li>';
});
Check as well this plugin: TableSorter
Upvotes: 0