The following code sorts an HTML table with JavaScript (without using any external libraries like jQuery). Are there any shortcomings or possible improvements I could make?
<html><head> <meta http-equiv="content-type" content="text/html;charset=Windows-1252"> <script type="text/javascript"> var people, asc1 = 1, asc2 = 1, asc3 = 1; window.onload = function () { people = document.getElementById("people"); } function sort_table(tbody, col, asc) { var rows = tbody.rows, rlen = rows.length, arr = new Array(), i, j, cells, clen; // fill the array with values from the table for (i = 0; i < rlen; i++) { cells = rows[i].cells; clen = cells.length; arr[i] = new Array(); for (j = 0; j < clen; j++) { arr[i][j] = cells[j].innerHTML; } } // sort the array by the specified column number (col) and order (asc) arr.sort(function (a, b) { return (a[col] == b[col]) ? 0 : ((a[col] > b[col]) ? asc : -1 * asc); }); // replace existing rows with new rows created from the sorted array for (i = 0; i < rlen; i++) { rows[i].innerHTML = "<td>" + arr[i].join("</td><td>") + "</td>"; } } </script> <style type="text/css"> table { border-collapse: collapse; border: none; } th, td { border: 1px solid black; padding: 4px 16px; font-family: Times New Roman; font-size: 24px; text-align: left; } th { background-color: #C8C8C8; cursor: pointer; } </style></head><body> <table> <thead> <tr> <th>Name</th> <th>Surname</th> <th>Age</th> </tr> </thead> <tbody> <tr> <td>Raja</td> <td>Dey</td> <td>18</td> </tr> <tr> <td>Mamata</td> <td>Sharma</td> <td>20</td> </tr> <tr> <td>Avijit</td> <td>Sharma</td> <td>21</td> </tr> <tr> <td>Sharanya</td> <td>Dutta</td> <td>26</td> </tr> <tr> <td>Nabin</td> <td>Roy</td> <td>27</td> </tr> </tbody> </table></body></html>- 5\$\begingroup\$Is the code otherwise working? If you're looking for just feedback, you should post toCode Review instead.\$\endgroup\$JJJ– JJJ2013-12-17 09:37:37 +00:00CommentedDec 17, 2013 at 9:37
- \$\begingroup\$Why not use Jquery. You can use tablesorter plugin.tablesorter.com/docs/example-pager.html\$\endgroup\$Anup– Anup2013-12-17 10:14:58 +00:00CommentedDec 17, 2013 at 10:14
- 1\$\begingroup\$His code is working (it only had a misconfiguration on jsfiddle:jsfiddle ) and he is asking for performance tips. @Anup :
(without any external library like JQuery)\$\endgroup\$ProGM– ProGM2013-12-17 10:20:37 +00:00CommentedDec 17, 2013 at 10:20 - \$\begingroup\$Instead of
-1 * asc, use just-asc.\$\endgroup\$David R Tribble– David R Tribble2016-11-08 18:56:58 +00:00CommentedNov 8, 2016 at 18:56 - \$\begingroup\$Why not using UTF-8 instead of this weird charset?\$\endgroup\$gouessej– gouessej2016-11-22 10:53:41 +00:00CommentedNov 22, 2016 at 10:53
5 Answers5
To speed up the sorting you first have to find what is consuming time. In your case the slower part of your code is:
for(i = 0; i < rlen; i++){ rows[i].innerHTML = "<td>"+arr[i].join("</td><td>")+"</td>";}The reason is that the DOM elaboration is time consuming for the browser.
Here's an updated version that minimizes the access to the DOM:
function sort_table(tbody, col, asc){ var rows = tbody.rows, rlen = rows.length, arr = new Array(), i, j, cells, clen; // fill the array with values from the table for(i = 0; i < rlen; i++){ cells = rows[i].cells; clen = cells.length; arr[i] = new Array(); for(j = 0; j < clen; j++){ arr[i][j] = cells[j].innerHTML; } } // sort the array by the specified column number (col) and order (asc) arr.sort(function(a, b){ return (a[col] == b[col]) ? 0 : ((a[col] > b[col]) ? asc : -1*asc); }); for(i = 0; i < rlen; i++){ arr[i] = "<td>"+arr[i].join("</td><td>")+"</td>"; } tbody.innerHTML = "<tr>"+arr.join("</tr><tr>")+"</tr>";}- \$\begingroup\$why is your method slower than op s method on firefox 61 osx 10.14 lol\$\endgroup\$PirateApp– PirateApp2018-07-10 14:38:53 +00:00CommentedJul 10, 2018 at 14:38
I ran into some trouble withProGM's solution.
- It didn't take numerical sorting into account.
- IE did NOT like setting this line:
tbody.innerHTML = "<tr>"+arr.join("</tr><tr>")+"</tr>"; - It would destroy any attributes assigned to any part of the table (css class names and etc)
I have made adjustments:
function sort_table(tbody, col, asc){ var rows = tbody.rows; var rlen = rows.length; var arr = new Array(); var i, j, cells, clen; // fill the array with values from the table for(i = 0; i < rlen; i++) { cells = rows[i].cells; clen = cells.length; arr[i] = new Array(); for(j = 0; j < clen; j++) { arr[i][j] = cells[j].innerHTML; } } // sort the array by the specified column number (col) and order (asc) arr.sort(function(a, b) { var retval=0; var fA=parseFloat(a[col]); var fB=parseFloat(b[col]); if(a[col] != b[col]) { if((fA==a[col]) && (fB==b[col]) ){ retval=( fA > fB ) ? asc : -1*asc; } //numerical else { retval=(a[col] > b[col]) ? asc : -1*asc;} } return retval; }); for(var rowidx=0;rowidx<rlen;rowidx++) { for(var colidx=0;colidx<arr[rowidx].length;colidx++){ tbody.rows[rowidx].cells[colidx].innerHTML=arr[rowidx][colidx]; } }}To address point #1, I added in the calls toparseFloat() and then compared the result the original value (You change to checking if it producesNaN instead). If both values are numeric, they compared via numerical preference and not by their string versions.
For points #2 and #3, I solved it via the same set of code. Instead of destroying the whole table body by setting theinnerHTML, I change the individual cell contents to the new sorted values:
for(var rowidx=0;rowidx<rlen;rowidx++){ for(var colidx=0;colidx<arr[rowidx].length;colidx++) { tbody.rows[rowidx].cells[colidx].innerHTML=arr[rowidx][colidx]; }}That only works if yours all have the same number of columns, but if you do not, sorting is much more complex anyway.
- \$\begingroup\$will you explain the adjustments please and how they overcome the issues that you found?\$\endgroup\$Malachi– Malachi2014-11-10 17:12:29 +00:00CommentedNov 10, 2014 at 17:12
- 1\$\begingroup\$Can do, I also found a mistake in mine.\$\endgroup\$Plater– Plater2014-11-11 13:51:59 +00:00CommentedNov 11, 2014 at 13:51
Here is the pure JS for sorting table data. I used first column which holds numbers. You have to modifycolumn index and condition statement as per your requirements. I hope that solves your problem...
// Table data sorting starts.... function sortData() { // Read table body node. var tableData = document.getElementById('data_table').getElementsByTagName('tbody').item(0); // Read table row nodes. var rowData = tableData.getElementsByTagName('tr'); for(var i = 0; i < rowData.length - 1; i++) { for(var j = 0; j < rowData.length - (i + 1); j++) { //Swap row nodes if short condition matches if(parseInt(rowData.item(j).getElementsByTagName('td').item(0).innerHTML) > parseInt(rowData.item(j+1).getElementsByTagName('td').item(0).innerHTML)) { tableData.insertBefore(rowData.item(j+1),rowData.item(j)); } } } } // Table data sorting ends....HTML Table:
<table width="200" border="1"> <tbody> <tr> <td width="100">01</td> <td>aaa</td> </tr> <tr> <td>04</td> <td>ddd</td> </tr> <tr> <td>05</td> <td>eee</td> </tr> <tr> <td>03</td> <td>ccc</td> </tr> <tr> <td>02</td> <td>bbb</td> </tr> <tr> <td>06</td> <td>fff</td> </tr> </tbody></table>HTML Table: Sorted
<table width="200" border="1"> <tbody> <tr> <td width="100">01</td> <td>aaa</td> </tr> <tr> <td>02</td> <td>bbb</td> </tr> <tr> <td>03</td> <td>ccc</td> </tr> <tr> <td>04</td> <td>ddd</td> </tr> <tr> <td>05</td> <td>eee</td> </tr> <tr> <td>06</td> <td>fff</td> </tr> </tbody></table>- \$\begingroup\$This whole answer is all jquery. Although I think it's great you answered, the OP was asking specifically for non jquery solutions.\$\endgroup\$dreza– dreza2013-12-21 05:33:49 +00:00CommentedDec 21, 2013 at 5:33
- \$\begingroup\$Here you go. Pure JS code for sorting HTML nodes. You can see the demo on given link.\$\endgroup\$Airan– Airan2013-12-22 11:18:32 +00:00CommentedDec 22, 2013 at 11:18
I was working on a table sorting script in ES6 today, researching the various methods to determine the most efficient. I dismissed working with theinnerHTML property out of hand, fromprevious research, instead trying a series of options using:
with the latter coming out as the fastest in my tests on tables with a large number (>5000) of rows.
Given that, here is the code I came up with, integrated into your use case.
(()=>{ let people=document.getElementById("people"), thead=people.previousElementSibling, rows=[...people.rows], orders=[1,1,-1], sort=(col)=>{ let x=rows.length; rows.sort((a,b)=>{ let i=a.children[col].firstChild.nodeValue, j=b.children[col].firstChild.nodeValue; return i===j?0:i>j?orders[col]:-orders[col]; }); orders[col]*=-1; while(people.lastChild) people.lastChild.remove(); while(x--) people.prepend(rows[x]); }; thead.addEventListener("click",event=>{ let target=event.target; if(target.nodeName.toLowerCase()==="th") sort(target.cellIndex); },0);})();table{ border-collapse:collapse; border:none;}th,td{ border:1px solid black; padding:4px 16px; font-family:Times New Roman; font-size:24px; text-align:left;}th{ background-color:#C8C8C8; cursor:pointer;}<table> <thead> <tr> <th>Name</th> <th>Surname</th> <th>Age</th> </tr> </thead> <tbody> <tr> <td>Raja</td> <td>Dey</td> <td>18</td> </tr> <tr> <td>Mamata</td> <td>Sharma</td> <td>20</td> </tr> <tr> <td>Avijit</td> <td>Sharma</td> <td>21</td> </tr> <tr> <td>Sharanya</td> <td>Dutta</td> <td>26</td> </tr> <tr> <td>Nabin</td> <td>Roy</td> <td>27</td> </tr> </tbody></table>- \$\begingroup\$Thank you. I just converted your script into Javascript of "nowadays", I used ".tHead" on the table object instead of previousElementSibling and I added a null check because a.children[col].firstChild.nodeValue can be null when a cell is empty. I'm going to compute the content of "orders".\$\endgroup\$gouessej– gouessej2016-11-17 16:33:26 +00:00CommentedNov 17, 2016 at 16:33
- \$\begingroup\$prepend is an experimental feature not supported by Microsoft Edge. Array.from() isn't supported by Microsoft Internet Explorer.\$\endgroup\$gouessej– gouessej2016-11-22 10:55:04 +00:00CommentedNov 22, 2016 at 10:55
- \$\begingroup\$I modified the loop in order to use appendChild() instead of prepend(), I used removeChild() instead of remove() and I replaced Array.from by a simple creation of an array and a loop to copy the elements one by one. Now, it works in Mozilla Firefox, Google Chrome & Chromium, Microsoft Edge and Microsoft Internet Explorer.\$\endgroup\$gouessej– gouessej2016-11-22 14:06:14 +00:00CommentedNov 22, 2016 at 14:06
Here is an improved version based on Plater's anwser. Just in case there is DOM elements inside oftd tags, the code gets the last child of the cell and get the value, and apply a customized filter to it.
Note: The customized filter here is just an example, and also you don't need to get the last child of thetd tags, and it can be any child of your use case.
function sortTable(tbody, col, asc) { var rows = tbody.rows; var rlen = rows.length; var arr = new Array(); var i, j, cells, clen; /* fill the array with values from the table */ for (i = 0; i < rlen; i++) { cells = rows[i].cells; clen = cells.length; arr[i] = new Array(); for (j = 0; j < clen; j++) { arr[i][j] = cells[j].innerHTML; } } /* sort the array by the specified column number (col) and order (asc) */ arr.sort(function(a, b) { var retval = 0; var aVal = getDomValue(a[col]); var bVal = getDomValue(b[col]); var fA = parseFloat(aVal); var fB = parseFloat(bVal); if (aVal != bVal) { if ((fA == aVal) && (fB == bVal)) { retval = (fA > fB) ? asc : -1 * asc; } // Numerical else { retval = (aVal > bVal) ? asc : -1 * asc; } // String } return retval; }); /* fill the table with sorted values */ for (var rowidx = 0; rowidx < rlen; rowidx++) { for (var colidx = 0; colidx < arr[rowidx].length; colidx++) { tbody.rows[rowidx].cells[colidx].innerHTML = arr[rowidx][colidx]; } }}function getDomValue(domString) { var value; var parser = new DOMParser(); var element = parser.parseFromString(domString, 'text/xml'); var content = element.lastChild.innerHTML; // Custom filter just in case if there is elements inside of the td. // I made two filters to standardize numbers like '$20,000', also I // treat whereas ' ' space as '0' if (content === ' ') content = content.replace(' ', '0'); if (content.indexOf('$') !== -1) { content = content.replace('$', ''); content = content.replace(',', ''); } value = isNaN(parseFloat(content)) ? content : parseFloat(content); return value;}You mustlog in to answer this question.
Explore related questions
See similar questions with these tags.


