DriversRecommendedOutdated drivers can make a good PC feel brokenScan driver issues before chasing fixes manually.Scan NowOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PC×
Skip to content
World desk5 min

How to Make an HTML Table Sortable with JavaScript

Use JavaScript to sort table rows by a selected column, with numeric and text comparators, keyboard-operable buttons, and accessible direction state.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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. Find the table and its body. The rows are sorted within the first <tbody>.
  2. 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.
  3. Compare values using their data type. The score column uses numeric subtraction; the name column uses localeCompare().
  4. Move the sorted rows back into the body. Appending existing row elements changes their order without rebuilding their contents.
  5. Expose the active direction. The script sets aria-sort on 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.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Dates 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.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Crashes, No Sound, or Screen Glitches?Free driver scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.