6

I'm writing some JS to sort an HTML table: the HTML table has a header row, clicking on the header of a column sorts the rows according to that column. My general algorithm is as follows:

  1. Convert HTML into a JavaScript array.
  2. Sort JavaScript array.
  3. Reconvert JavaScript array to HTML table.

Right now, my code replaces all of the innerHTML elements of the table. Basically, after it sorts the JS array, it touches every node of the table, rewriting it's innerHTML value. I feel like there might be a more efficient way to do this, but I can't think of it.

EDIT:

Here's the code that does part (3) of the algorithm outlined above. I edited out the irrelevant parts (just some string transformations). The indices are a little weird because I skipped the header row when I converted the table to an array, so I'm compensating for that when rewriting the table.

for (var i = 0; i < numRows-1; i++){
 for (var j = 0; j < numCols; j++){
        var arrCell = tableArr[i][cols[j]];
        tableEl.rows[i+1].cells[j].innerHTML = arrCell;
    }
}
6
  • Do you want to achieve this in pure JS only? Commented Aug 8, 2012 at 14:34
  • As long as you're generating the new markup in memory and then appending it with only 1 DOM operation, this should be pretty efficient. DOM modifications are far slower than Javascript array operations. Commented Aug 8, 2012 at 14:38
  • @Ghillied Yes. I mean, is there a more efficient way to do this outside of pure JS? Are you suggesting that I turn the header rows into links and re-GET the page from the server with the table sorted? Commented Aug 8, 2012 at 15:03
  • @ChrisFrancis For a table of n nodes I'm doing n DOM operations (i.e. I'm rewriting the innerHTML). Commented Aug 8, 2012 at 15:06
  • @Avery There isn't a more efficent way to do this other than pure JS. I gave you a way to do it with a library, but using libraries will load additional kilobytes into your page for nothing (since you will not use the other functions inside the library). Commented Aug 8, 2012 at 15:07

4 Answers 4

3

Use an array to sort the values and a document fragment to perform the update.

function sortRows(tbody, compare, sortDesc) {
    //convert html collection to array
    var rows = [].slice.call(tbody.rows);
    //sort to desired order
    rows.sort(compare);
    if (sortDesc) {
        rows.reverse();
    }
    //update table
    var fragment = document.createDocumentFragment();
    rows.forEach(function (row) {
        fragment.appendChild(row);
    });
    tbody.appendChild(fragment);
}

The complexity will be in the compare function. You will need to take into account the column index and any type conversions and caching you want.

This is a basic example that converts the cell text content to an integer.

function sortTable(table, columnIndex) {
    while (table && table.tagName !== 'TABLE') {
        table = table.parentNode;
    }
    if (table) {
        sortRows(table.tBodies[0], function compare(topRow, bottomRow) {
            var topValue = parseInt(topRow.cells[columnIndex].textContent, 10);
            var bottomValue = parseInt(bottomRow.cells[columnIndex].textContent, 10);
            return (topValue - bottomValue);
        });
    }
}

function sortRows(tbody, compare, sortDesc) {
    //convert html collection to array
    var rows = [].slice.call(tbody.rows);
    //sort to desired order
    rows.sort(compare);
    if (sortDesc) {
        rows.reverse();
    }
    //update table
    var fragment = document.createDocumentFragment();
    rows.forEach(function (row) {
        fragment.appendChild(row);
    });
    tbody.appendChild(fragment);
}
<table>
    <thead>
        <tr><th onclick="sortTable(this, 0)">Sort</th><th onclick="sortTable(this, 1)">Sort</th></tr>
    </thead>
    <tbody>
        <tr><td>1</td><td>25</td></tr>
        <tr><td>3</td><td>12</td></tr>
        <tr><td>2</td><td>40</td></tr>
        <tr><td>10</td><td>25</td></tr>
    </tbody>
</table>

Sign up to request clarification or add additional context in comments.

1 Comment

If you need to work with IE8 then replace [].slice.call(), rows.forEach() and cell.textContent with cross browser methods/properties.
2

You could remove the rows from the DOM, do the sort, then add them back onto the DOM.

var tbody = table.tBodies[0];

var placeholder = document.createElement('tbody');
table.replaceChild(placeholder, tbody);

//sort rows in tbody

table.replaceChild(tbody, placeholder);

1 Comment

I don't why this was down voted. It is faster to remove an element from the DOM, make a lot of changes to it (like moving elements by a sort) and then add it back into the DOM. This results in two page redraws instead of a page redraw for every time you move a row.
1

You can use jQuery and its Datatables plugin. In your datatable statement, you can add

"bSort": true

and the job will be done by the script. Although if you can find a pure JS way I suggest you use it so you don't add useless weight to your web page.

Comments

0

If the data is stored on the server - I would send an Ajax request to the server with the sorting type (ASC, DESC, etc. ), sort the data on the server (using PHP, etc) and then receive the sorted data in JavaScript and write the table completely new.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.