简体   繁体   中英

HTML table sortable by ROW instead of COLUMN with JavaScript?

I'm using this code from W3Schools for sorting all table rows by clicking on column header. As expected, this orders all rows by that col value.

I'm shure this has already been answered somewere but I't has been difficult for me to filter serarch results to find a similar way (hopefully in pure javascript) of sorting columns by clicking on a first column row.

That should order all columns by that clicked row values.

I'm hoping for some combined method that could order by column AND by row depending on user click, but just a Sort by Row method would be OK.

Thanks in advace!

Expected behavior

Unsorted table:

<table id="myTable">
  <tbody>
    <tr>
      <th onclick="sortTableRows(0)">Name</th>
      <th onclick="sortTableRows(1)">Col 1</th>
      <th onclick="sortTableRows(2)">Col 2</th>
      <th onclick="sortTableRows(3)">Col 3</th>
    </tr>
    <tr>
      <td onclick="sortTableCols(1)">Alan Brado</td>
      <td>2</td>
      <td>3</td>
      <td>1</td>
    </tr>
    <tr>
      <td onclick="sortTableCols(2)">Kevin Chuca</td>
      <td>1</td>
      <td>3</td>
      <td>2</td>
    </tr>
    <tr>
      <td onclick="sortTableCols(3)">Pamela Chu</td>
      <td>3</td>
      <td>2</td>
      <td>1</td>
    </tr> 
  </tbody>
</table>

NORMAL : Sorted table by COLUMN (Col 1):

<table id="myTable">
  <tbody>
    <tr>
      <th onclick="sortTableRows(0)">Name</th>
      <th onclick="sortTableRows(1)">*Col 1*</th>
      <th onclick="sortTableRows(2)">Col 2</th>
      <th onclick="sortTableRows(3)">Col 3</th>
    </tr>
    <tr>
      <td onclick="sortTableCols(1)">Kevin Chuca</td>
      <td>1</td>
      <td>3</td>
      <td>2</td>
    </tr>
    <tr>
      <td onclick="sortTableCols(1)">Alan Brado</td>
      <td>2</td>
      <td>3</td>
      <td>1</td>
    </tr>
    <tr>
      <td onclick="sortTableCols(3)">Pamela Chu</td>
      <td>3</td>
      <td>2</td>
      <td>1</td>
    </tr> 
  </tbody>
</table>

DESIRED : Sorted table by ROW (Alan Brado):

<table id="myTable">
  <tbody>
    <tr>
      <th onclick="sortTableRows(0)">Name</th>
      <th onclick="sortTableRows(1)">Col 3</th>
      <th onclick="sortTableRows(2)">Col 1</th>
      <th onclick="sortTableRows(3)">Col 2</th>
    </tr>
    <tr>
      <td onclick="sortTableCols(1)">*Alan Brado*</td>
      <td>1</td>
      <td>2</td>
      <td>3</td>
    </tr>
    <tr>
      <td onclick="sortTableCols(2)">Kevin Chuca</td>
      <td>2</td>
      <td>1</td>
      <td>3</td>
    </tr>
    <tr>
      <td onclick="sortTableCols(3)">Pamela Chu</td>
      <td>1</td>
      <td>3</td>
      <td>2</td>
    </tr> 
  </tbody>
</table>

And here is my snippet:

 function sortTableRows(n) { var table, rows, switching, i, x, y, shouldSwitch, dir, switchcount = 0; table = document.getElementById("myTable"); switching = true; dir = "asc"; while (switching) { switching = false; rows = table.getElementsByTagName("TR"); for (i = 1; i < (rows.length - 1); i++) { shouldSwitch = false; x = rows[i].getElementsByTagName("TD")[n]; y = rows[i + 1].getElementsByTagName("TD")[n]; if (dir == "asc") { if (x.innerHTML.toLowerCase() > y.innerHTML.toLowerCase()) { shouldSwitch= true; break; } } else if (dir == "desc") { if (x.innerHTML.toLowerCase() < y.innerHTML.toLowerCase()) { shouldSwitch= true; break; } } } if (shouldSwitch) { rows[i].parentNode.insertBefore(rows[i + 1], rows[i]); switching = true; switchcount ++; } else { if (switchcount == 0 && dir == "asc") { dir = "desc"; switching = true; } } } } function sortTableCols(n) { alert("This should sort cols by row #"+n+" values"); } 
  /* Just for beauty */ #myTable { border: none; } #myTable th { cursor: pointer; width: 5%; border: none; background-color: #e0e0e0; } #myTable tr td { border: none; border-bottom: 1px solid #aaa; text-align: center; } #myTable tr td:first-child { cursor: pointer; background-color: #e0e0e0; border-bottom: 1px solid #aaa; text-align: center; } #myTable tr:last-child td { border: none; } 
 <table id="myTable"> <tbody> <tr> <th onclick="sortTableRows(0)">Name</th> <th onclick="sortTableRows(1)">Col 1</th> <th onclick="sortTableRows(2)">Col 2</th> <th onclick="sortTableRows(3)">Col 3</th> </tr> <tr> <td onclick="sortTableCols(1)">Alan Brado</td> <td>2</td> <td>3</td> <td>1</td> </tr> <tr> <td onclick="sortTableCols(2)">Kevin Chuca</td> <td>1</td> <td>3</td> <td>2</td> </tr> <tr> <td onclick="sortTableCols(3)">Pamela Chu</td> <td>3</td> <td>2</td> <td>1</td> </tr> </tbody> </table> 

Update: See @sanxofon's answer. Is an improvement of mine.


No answers for the time being so I ended up solving the issue using this procedure:

  1. Capturing table to matrix

  2. Transpose matrix (flip axis)

  3. Sort matrix by column (as normally)

  4. Transpose matrix back (flip axis again)

  5. Update matrix to table

I post this for any who lands here that may be interested in a solution.

Performance: I haven't tested it for performance, but as all process are done on matrix it depends entirely on the length of it and of its data. Works quite good and can be implemented on any table.

ToDo: Could be improved by adding listeners for row and column headers and removing onClick="sortTable(x,y)" from table.

Here is it:

 var dirc = 0, dirr = 0, cc = 0, rr = 0; function colSlice(arr,ini=0,fin=0) { if (ini<0) ini = arr.length+ini; if (fin==0) fin = arr.length; else if (fin<0) fin = arr.length+fin; else fin = ini+fin var sliced = []; for (var i = 0; i < arr.length; i++) { sliced.push([]); for (var j = 0; j < arr[i].length; j++) { if (j>=ini && j<fin) sliced[i].push(arr[i][j]); } } return sliced; } function colJoin(a,b) { var joined = []; for (var i = 0; i < a.length; i++) { joined.push([]); for (var j = 0; j < a[i].length; j++) { joined[i].push(a[i][j]); } for (var j = 0; j < b[i].length; j++) { joined[i].push(b[i][j]); } } return joined; } function sortTable(r,c) { var table = document.getElementById("myTable"); var rows = table.getElementsByTagName("TR"); var matriz = []; var m = null; // Get matrix from table for (i = 0; i < rows.length; i++) { if (i==0) cols = rows[i].getElementsByTagName("TH"); else cols = rows[i].getElementsByTagName("TD"); m = []; for (j = 0; j < cols.length; j++) { m.push(cols[j].innerHTML); } matriz.push(m); } if (r==0) { // sort rows by col if (c==cc) dirc = Math.abs(1-dirc); else dirc = 0; m = matriz[0]; matriz = matriz.slice(1); matriz.sort(function(a, b){ if (dirc<=0) { if (a[c].toLowerCase() > b[c].toLowerCase()) return 1; else if (a[c].toLowerCase() < b[c].toLowerCase()) return -1; } else { if (a[c].toLowerCase() > b[c].toLowerCase()) return -1; else if (a[c].toLowerCase() < b[c].toLowerCase()) return 1; } return 0; }); matriz.unshift(m); cc = c; } if (c==0) { // sort cols by row if (r==rr) dirr = Math.abs(1-dirr); else dirr = 0; m = colSlice(matriz,0,1); matriz = colSlice(matriz,1); // Transpose matriz var newArray = matriz[0].map(function(col, i){ return matriz.map(function(row){ return row[i]; }); }); // Sort newArray.sort(function(a, b){ if (dirr<=0) { if (a[r].toLowerCase() > b[r].toLowerCase()) return 1; else if (a[r].toLowerCase() < b[r].toLowerCase()) return -1; } else { if (a[r].toLowerCase() > b[r].toLowerCase()) return -1; else if (a[r].toLowerCase() < b[r].toLowerCase()) return 1; } return 0; }); // Transpose back matriz = newArray[0].map(function(col, i){ return newArray.map(function(row){ return row[i]; }); }); matriz = colJoin(m,matriz); rr = r; } // Update values for (i = 0; i < rows.length; i++) { if (i==0) cols = rows[i].getElementsByTagName("TH"); else cols = rows[i].getElementsByTagName("TD"); for (j = 0; j < cols.length; j++) { cols[j].innerHTML = matriz[i][j]; } } } 
 /* Just for beauty */ #myTable { border: none; } #myTable th { cursor: n-resize; border: none; background-color: #e0e0e0; } #myTable th:first-child { cursor: move; } th, td { width: 5%; } #myTable tr td { border: none; border-bottom: 1px solid #aaa; text-align: center; } #myTable tr td:first-child { cursor: e-resize; background-color: #e0e0e0; border-bottom: 1px solid #aaa; text-align: center; } #myTable tr:last-child td { border: none; } 
 <table id="myTable"> <tbody> <tr> <th onclick="sortTable(0,0)">Both</th> <th onclick="sortTable(0,1)">Col A</th> <th onclick="sortTable(0,2)">Col B</th> <th onclick="sortTable(0,3)">Col C</th> </tr> <tr> <td onclick="sortTable(1,0)">Row A</td> <td>2</td> <td>3</td> <td>1</td> </tr> <tr> <td onclick="sortTable(2,0)">Row B</td> <td>1</td> <td>3</td> <td>2</td> </tr> <tr> <td onclick="sortTable(3,0)">Row C</td> <td>3</td> <td>2</td> <td>1</td> </tr> </tbody> </table> 

Cheers!

I often asked my self that question but never seem to really need it so never went any further. Your answer is great, but you'll have issues when comparing numbers as strings. It does not shows in your code because you are using 1, 2, 3 values. Check my snippet with mixed data types, of course the code can be reduced a lot.

 var dirc = 0, dirr = 0, cc = 0, rr = 0; function colSlice(arr,ini=0,fin=0) { if (ini<0) ini = arr.length+ini; if (fin==0) fin = (arr.length+1)-ini; else fin = ini+fin var sliced = []; for (var i = 0; i < arr.length; i++) { sliced.push([]); for (var j = 0; j < arr[i].length; j++) { if (j>=ini && j<fin) if (parseFloat(arr[i][j])>0 || arr[i][j]=="0") sliced[i].push(parseFloat(arr[i][j])); else sliced[i].push(arr[i][j]); } } return sliced; } function colJoin(a,b) { var joined = []; for (var i = 0; i < a.length; i++) { joined.push([]); for (var j = 0; j < a[i].length; j++) { joined[i].push(a[i][j]); } for (var j = 0; j < b[i].length; j++) { joined[i].push(b[i][j]); } } return joined; } function sortMatrix(a,b,x,d) { if (d<=0) var ret = [1,-1]; else var ret = [-1,1]; if (isNaN(a[x]) && isNaN(b[x])) { var ax = a[x].toLowerCase(); var bx = b[x].toLowerCase(); } else if (isNaN(a[x])) { return ret[0]; } else if (isNaN(b[x])) { return ret[1]; } else { var ax = parseFloat(a[x]); var bx = parseFloat(b[x]); } if (ax > bx) return ret[0]; else if (ax < bx) return ret[1]; else return 0; } function sortTable(r,c) { var table = document.getElementById("myTable"); var rows = table.getElementsByTagName("TR"); var matriz = []; var m = null; // Get matrix from table for (i = 0; i < rows.length; i++) { if (i==0) cols = rows[i].getElementsByTagName("TH"); else cols = rows[i].getElementsByTagName("TD"); m = []; for (j = 0; j < cols.length; j++) { m.push(cols[j].innerHTML); } matriz.push(m); } if (r==0) { // sort rows by col if (c==cc) dirc = Math.abs(1-dirc); else dirc = 0; m = matriz[0]; matriz = matriz.slice(1); matriz.sort(function(a, b){ return sortMatrix(a,b,c,dirc); }); matriz.unshift(m); cc = c; } if (c==0) { // sort cols by row if (r==rr) dirr = Math.abs(1-dirr); else dirr = 0; m = colSlice(matriz,0,1); matriz = colSlice(matriz,1); // Transpose matriz var newArray = matriz[0].map(function(co, i){ return matriz.map(function(ro){ return ro[i]; }); }); // Sort newArray.sort(function(a, b){ return sortMatrix(a,b,r,dirr); }); // Transpose back matriz = newArray[0].map(function(co, i){ return newArray.map(function(ro){ return ro[i]; }); }); matriz = colJoin(m,matriz); rr = r; } // Update values for (i = 0; i < rows.length; i++) { if (i==0) cols = rows[i].getElementsByTagName("TH"); else cols = rows[i].getElementsByTagName("TD"); for (j = 0; j < cols.length; j++) { cols[j].innerHTML = matriz[i][j]; } } } function clicRow(e) { sortTable(0,parseInt(e.target.attributes.col.value)); } var tds=document.querySelectorAll("#myTable tr td:first-child,#myTable tr th:first-child"); var len = tds.length; for(var i=0; i< len; i++){ tds[i].addEventListener('click', clicRow); } function clicCol(e) { sortTable(parseInt(e.target.attributes.row.value),0); } var tds=document.querySelectorAll("#myTable tr:first-child th"); var len = tds.length; for(var i=1; i< len; i++){ tds[i].addEventListener('click', clicCol); } 
  /* Just for beauty */ #myTable { border: none; } #myTable th { cursor: n-resize; border: none; background-color: #e0e0e0; } #myTable th:first-child { cursor: move; } th, td { width: 5%; } #myTable tr td { border: none; border-bottom: 1px solid #aaa; text-align: center; } #myTable tr td:first-child { cursor: e-resize; background-color: #e0e0e0; border-bottom: 1px solid #aaa; text-align: center; } #myTable tr:last-child td { border: none; } 
 <table id="myTable"> <tbody> <tr> <th row="0" col="0">Both</th> <th row="1">Col A</th> <th row="2">Col B</th> <th row="3">Col C</th> <th row="4">Col 4</th> </tr> <tr> <td col="1">Row A</td> <td>221</td> <td>2</td> <td>22</td> <td>22.2</td> </tr> <tr> <td col="2">Row B</td> <td>123</td> <td>1234</td> <td>ABA</td> <td>aba</td> </tr> <tr> <td col="3">Row C</td> <td>1</td> <td>12.04</td> <td>12.4</td> <td>ab</td> </tr> <tr> <td col="4">Row 4</td> <td>123.2</td> <td>123.1</td> <td>acar</td> <td>acar</td> </tr> </tbody> </table> 

Update: Simplified and removed onClick calls.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM