The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →To sort an HTML table when a reader clicks a column heading, collect the rows in its <tbody>, sort them with a comparator suited to that column’s values, then append the rows back into the body. The example below adds text and numeric sorting, ascending/descending toggling, and an accessible sort state.
Build a semantic table with sortable controls
Keep column headings in <thead> and data rows in <tbody>. Use a real button inside each sortable header: it can be reached and activated with a keyboard, unlike a heading that only has a click handler.
As an Amazon Associate I earn from qualifying purchases.
<table>
<caption>Scores. Activate a column heading to sort the table.</caption>
<thead>
<tr>
<th scope="col"><button type="button" data-sort="name">Name</button></th>
<th scope="col"><button type="button" data-sort="score">Score</button></th>
</tr>
</thead>
<tbody>
<tr><td>Alex</td><td>15</td></tr>
<tr><td>Devlin</td><td>12</td></tr>
<tr><td>Sam</td><td>18</td></tr>
</tbody>
</table>
The caption tells users what the controls do. In a larger page, you can instead provide a nearby instruction associated with the table, provided it is clear to both sighted and screen-reader users.
Sort rows when a heading button is activated
This script assumes one header row, one table body, and plain text values in each cell. It starts a newly selected column in ascending order; clicking the same column again reverses the order.
#1 Best Overall
- Find the table and its body. The rows are sorted within the first
<tbody>. - Map each button to its column. The header cell’s position is used as the body-cell index, so the header and data columns must line up.
- Compare values using their data type. The score column uses numeric subtraction; the name column uses
localeCompare(). - Move the sorted rows back into the body. Appending existing row elements changes their order without rebuilding their contents.
- Expose the active direction. The script sets
aria-sorton the sorted header and removes it from the others.
const table = document.querySelector("table");
const tbody = table.tBodies[0];
const buttons = table.querySelectorAll("thead button[data-sort]");
let activeColumn = null;
let ascending = true;
for (const button of buttons) {
button.addEventListener("click", () => {
const key = button.dataset.sort;
const header = button.closest("th");
const columnIndex = Array.from(header.parentElement.cells).indexOf(header);
const rows = Array.from(tbody.rows);
if (activeColumn === key) {
ascending = !ascending;
} else {
activeColumn = key;
ascending = true;
}
rows.sort((rowA, rowB) => {
const a = rowA.cells[columnIndex].textContent.trim();
const b = rowB.cells[columnIndex].textContent.trim();
const result = key === "score"
? Number(a) - Number(b)
: a.localeCompare(b);
return ascending ? result : -result;
});
tbody.append(...rows);
for (const cell of table.tHead.rows[0].cells) {
cell.removeAttribute("aria-sort");
}
header.setAttribute("aria-sort", ascending ? "ascending" : "descending");
});
}
The script sorts an array of row elements; sort() is an array method, not a table method. Array.from(tbody.rows) creates a regular array from the DOM row collection before sorting. Appending the resulting elements to the same body moves those nodes into the new order.
Choose a comparator that matches the column
Text values
localeCompare() is a convenient text comparison and is used in MDN’s click-to-sort table example. It is preferable to comparing text with arithmetic, but you may need options or a normalized value when the table has specialized sorting rules.
Rank #2
Numbers and formatted values
JavaScript’s default Array.prototype.sort() comparison converts values to strings. Without a numeric comparator, values such as 2 and 10 are ordered lexically rather than numerically. For clean numeric strings, subtraction provides a numeric comparison, as in the score example. However, Number("") becomes zero, and strings with currency symbols, thousands separators, or localized decimal marks may not parse as intended. Validate or normalize such values before comparing; do not let blank or invalid entries silently sort as zero.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsDates and other data
Display text is not always a reliable sort key. A date shown as 10/2/2026, for example, can be ambiguous across date conventions. For dates, formatted numbers, or cells with nested markup, store or extract a consistent underlying value and compare that value rather than assuming textContent is already suitable. Keep the display intact while using the normalized value for ordering.
Keep comparisons consistent and predictable
A comparator returns a negative number when the first item comes before the second, a positive number when it comes after, and zero (or NaN) when the values compare equally. It should follow consistent ordering rules, including purity, reflexivity, antisymmetry, and transitivity; an incomplete comparator that returns only 1 and 0 can produce inconsistent results across JavaScript engines. MDN explains these requirements and the default behavior in its Array.prototype.sort() reference.
Sorting is stable under the ECMAScript specification since ECMAScript 2019: rows that compare equal retain their previous relative order. That does not remove the need to define how blanks, invalid values, or ties should be handled. The specification does not promise a particular time or space complexity for sort(); performance characteristics depend on the JavaScript implementation and the amount of data.
Rank #4
Make the sort state accessible
Buttons provide keyboard activation, while aria-sort communicates the current ordering to assistive technology. The active header should be the only one with aria-sort="ascending" or aria-sort="descending"; remove the attribute from other headers. Also show direction visually—for example, with an up or down indicator—and ensure the indicator is not the only way the state is conveyed. See the W3C WAI-ARIA APG sortable-table example and MDN’s guidance on aria-sort.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Test that keyboard users can reach and activate each button, and check the result with the assistive technology used by your audience. The table’s semantic structure should include <th scope="col"> for column headings and <td> for data cells.
Quick Recap
Best Value
Adapt the code for more complex tables
- Multiple header rows or spanning cells: The example calculates a column from a single header row. With grouped or spanning headings, create an explicit mapping from each button to its data column.
- Multiple
<tbody>sections: The example sorts only the first body. Decide whether groups must stay separate or be sorted together, then apply the ordering rule to the appropriate rows. - Server-side or paginated results: Sorting only the rows currently in the DOM cannot reorder records that have not been loaded. Send the sort column and direction to the data source and render the returned results.
- Large data sets: Sorting the DOM’s current rows may not suit the amount of data or update strategy. Choose an approach appropriate to the table’s data source and rendering needs; the JavaScript specification does not set a complexity guarantee for
sort(). - Rows with links or other nested elements: Moving the existing row nodes preserves their contents. Still, compare a deliberate sort key rather than assuming all visible cell text is a clean value.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




