简体   繁体   中英

Javascript sort table column on click

My intention is to sort a table column (only the clicked th's column) in ascending order when th is being clicked. On further click the exact table column shall be ordered in a descending order.

What I got so far:
The table is being sorted, but on click all three columns are being sorted - I only want one column to be sorted.

 function sortTable(n) { var table, rows, switching, i, x, y, shouldSwitch, dir, switchcount = 0; table = document.getElementById("table"); switching = true; dir = "asc"; while (switching) { switching = false; rows = table.rows; 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; } } } }
 <table id="table"> <tr> <th onclick="sortTable(0)">Nummer</th> <th onclick="sortTable(1)">Land</th> <th onclick="sortTable(2)">Name</th> </tr> <tr> <td>2</td> <td>Rumänien</td> <td>Dan</td> </tr> <tr> <td>1</td> <td>Deutschland</td> <td>Aaron</td> </tr> <tr> <td>3</td> <td>USA</td> <td>Peter</td> </tr> </table>

This also looks like extreme overkill for the description. If all you want to do is sort your rows, then the code for that is a few lines:

function compareValues(a, b) {
  // return -1/0/1 based on what you "know" a and b
  // are here. Numbers, text, some custom case-insensitive
  // and natural number ordering, etc. That's up to you.
  // A typical "do whatever JS would do" is:
  return (a<b) ? -1 : (a>b) ? 1 : 0;
}

function sortTable(colnum) {
  // get all the rows in this table:
  let rows = Array.from(table.querySelectorAll(`tr`));

  // but ignore the heading row:
  rows = rows.slice(1);

  // set up the queryselector for getting the indicated
  // column from a row, so we can compare using its value:
  let qs = `td:nth-child(${colnum})`;

  // and then just... sort the rows:
  rows.sort( (r1,r2) => {
    // get each row's relevant column
    let t1 = r1.querySelector(qs);
    let t2 = r2.querySelector(qs);

    // and then effect sorting by comparing their content:
    return compareValues(t1.textContent,t2.textContent);
  });

  // and then the magic part that makes the sorting appear on-page:
  rows.forEach(row => table.appendChild(row));
}

The magic part works because DOM nodes can only exist in one place, so if you appendChild it to something, it gets moved from wherever it was in the first place. As such, we sort in JS, and then we run appendChild in that sorted order, every row is simply moved from "whereever it was" to "the end of the child list", which means after we run through every row in rows , presto: all the rows are now reordered as needed.

Of course you're still on the hook for writing compareValues(a,b) yourself. I don't know what you want to do there, because maybe you want natural text compare, or maybe you want "if the values are numbers, compare as numbers, only if they're not compare as text", etc.

But one thing you should do is not use 1998 event handling. Don't use onclick , do this properly on the JS side. So in your HTML:

<tr>
  <th>Nummer</th>
  <th>Land</th>
  <th>Name</th>
</tr>

And then on the JS side:

table.querySelectorAll(`th`).forEach((th, position) => {
  th.addEventListener(`click`, evt => sortTable(position));
});

(and better yet, use <th><button>Nummer</button></th> and add the event listener based on th button , because that's what it is. If you have text that can be clicked to "do something", that's a button. Use the correct markup, and then just use CSS to style it however you want, eg no border, background:inherit, etc)

Maybe use a different approach. Store your data and sort the array before rendering.

 let tblData = [{ number: 2, land: 'Rumänien', name: 'Dan' }, { number: 1, land: 'Deutschland', name: 'Aaron' }, { number: 3, land: 'USA', name: 'Peter' } ]; const getRow = (data) => `<tr><td>${data.number}</td><td>${data.land}</td><td>${data.name}</td></tr>`; const sortNumber = (a, b) => a.number - b.number; const sortLand = (a, b) => (a.land === b.land) ? 0 : (a.land > b.land) ? 1 : -1; const sortName = (a, b) => (a.name === b.name) ? 0 : (a.name > b.name) ? 1 : -1; function buildTable(data) { document.querySelector('tbody').innerHTML = data.map(row => getRow(row)).join(''); } function sortTable(n) { let sort = sortNumber; switch (n) { case 1: sort = sortLand; break; case 2: sort = sortName; } console.log(n); buildTable(tblData.sort(sort)); } buildTable(tblData);
 <table id="table"> <thead> <tr> <th onclick="sortTable(0)">Nummer</th> <th onclick="sortTable(1)">Land</th> <th onclick="sortTable(2)">Name</th> </tr> </thead> <tbody> </tbody> </table>

When running your code, I get the results that I would expect. I wonder if it seems like the results aren't correct simply because your data is setup that way.

For example, your data is:

1   Deutschland Aaron
2   Rumänien    Dan
3   USA Peter

And sorting on Number, would have Deutschland First with Aaron, and both Deutschland and Aaron are alphabetically first before the rest of the data.

So its an illusion that the other columns are being sorted, when in fact they are actually just following your data structure.

Mike 'Pomax' Kamermans ' answer works very well except the sorted table will place the <tr> s outside the <tbody> tag, which is undesired. This can be easily solved by changing his answer to this:

function sortTable(colnum) {
  let tbody = table.querySelector(`tbody`);
  let rows = Array.from(table.querySelectorAll(`tr`));
  
  // other codes...
  
  rows.forEach(row => tbody.appendChild(row));
  table.appendChild(tbody);
}

Then the generated table should be working perfectly.

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