8. Creating a Sortable HTML Table with JavaScript#
Sorting data in a table is a fundamental feature that helps users interact with and interpret information more easily. In this tutorial, you’ll learn how to create a table that allows users to sort by columns in ascending or descending order by clicking on the column headers.
8.1. Overview#
We will create a simple HTML table that can be sorted by clicking on the headers. The JavaScript function we write will handle the sorting logic, allowing each column to be sorted both in ascending and descending order.
8.2. HTML Structure#
We’ll start by defining an HTML table. Here’s the HTML code:
This HTML structure defines a simple table with a header (
<thead>
) and body (<tbody>
). Each<th>
in the header row represents a column that can be sorted.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sorting Tables with JavaScript</title>
<link rel="stylesheet" href="./table_sort.css">
</head>
<body>
<table class="table table-sortable">
<thead>
<tr>
<th>Rank</th>
<th>Name</th>
<th>Age</th>
<th>Occupation</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Dom</td>
<td>35</td>
<td>Web Developer</td>
</tr>
<tr>
<td>2</td>
<td>Rebecca</td>
<td>29</td>
<td>Teacher</td>
</tr>
<tr>
<td>3</td>
<td>John</td>
<td>30</td>
<td>Civil Engineer</td>
</tr>
<tr>
<td>4</td>
<td>Andre</td>
<td>20</td>
<td>Dentist</td>
</tr>
</tbody>
</table>
<script src="./table_sort.js"></script>
</body>
</html>
8.3. CSS (Optional) for Sorted Columns#
You can add optional CSS styling to show users the sorted column and order. For example:
/* Table Styling */ .table { width: 100%; border-collapse: collapse; font-family: Arial, sans-serif; color: #333; } .table th, .table td { padding: 10px 15px; text-align: left; } .table th { background-color: #00796b; /* Green header background */ color: #ffffff; /* White text color */ font-weight: bold; } .table tbody tr { border-bottom: 1px solid #e0e0e0; } .table tbody tr:last-child { border-bottom: 3px solid #00796b; /* Green border to match the header */ } .table tbody tr:nth-child(even) { background-color: #f9f9f9; /* Light gray background for even rows */ } .table tbody tr:hover { background-color: #e0f7fa; /* Light blue background on hover */ } /* Style for sorting */ .table-sortable th{ cursor: pointer; } .table-sortable .th-sort-asc::after{ content: "\25b4"; } .table-sortable .th-sort-desc::after{ content: "\25be"; } .table-sortable .th-sort-asc::after, .table-sortable .th-sort-desc::after{ margin-left: 5px; }
8.4. JavaScript Sorting Function#
8.4.1. Basic Function Setup#
First, we’ll create a basic function to sort a table. This function will receive the
table element
, thecolumn index
to sort by, and anoptional parameter for sorting order
(ascending by default).Start by creating a function structure like this:
/** * Sorts an HTML table by a specific column. * @param {HTMLTableElement} table - The table to sort. * @param {number} column - The index of the column to sort. * @param {boolean} asc - Determines if the sorting will be ascending. */ function sortTableByColumn(table, column, asc = true) { console.log("Sorting column:", column, "Ascending:", asc); }
8.4.2. Identify and Capture Rows to Sort#
Next, we need to capture all rows inside the table’s body so we can sort them.
Add the following lines inside the function:
const tBody = table.tBodies[0]; const rows = Array.from(tBody.querySelectorAll("tr")); console.log("Rows captured for sorting:", rows);
Explanation:
tBody captures the
<tbody
> element of the table.rows stores an array of all
<tr>
elements (rows) within the table body.Use
console.log
here to verify that rows contains the table rows we want to sort. This step helps ensure that we’re targeting the correct rows before sorting.
8.4.3. Sorting Rows by Column Data#
Now, let’s sort the rows based on the data in the selected column. We’ll retrieve the text content of each cell within that column for comparison.
Update the function as follows:
const sortedRows = rows.sort((a, b) => { const aColText = a.querySelector(`td:nth-child(${column + 1})`).textContent.trim(); const bColText = b.querySelector(`td:nth-child(${column + 1})`).textContent.trim(); console.log("Comparing:", aColText, "and", bColText); return aColText > bColText ? (1) : (-1); });
Explanation:
aColText
andbColText
retrieve the text content of the selected column from two different rows (a
andb
) to compare them.return aColText > bColText ? (1) : (-1);
sorts rows in ascending order by default. You can switch it to descending by changing the comparison operator.
8.4.4. Sorting Direction (Ascending/Descending)#
To control the sorting direction, let’s add a modifier (
dirModifier
) that toggles between ascending and descending order based on theasc
parameter.Adjust the sorting function as follows:
const dirModifier = asc ? 1 : -1; const sortedRows = rows.sort((a, b) => { const aColText = a.querySelector(`td:nth-child(${column + 1})`).textContent.trim(); const bColText = b.querySelector(`td:nth-child(${column + 1})`).textContent.trim(); return aColText > bColText ? (1 * dirModifier) : (-1 * dirModifier); }); console.log("Sorted Rows:", sortedRows);
8.4.5. Clearing and Re-adding Sorted Rows to the Table#
Once rows are sorted, we’ll clear out the existing rows in the table and re-add them in the sorted order.
Add this code next:
// Remove all existing rows from the table body while (tBody.firstChild) { tBody.removeChild(tBody.firstChild); } // Append the sorted rows to the table body tBody.append(...sortedRows);
Explanation:
while (tBody.firstChild)
removes all rows from the<tbody
>.tBody.append(...sortedRows);
adds the sorted rows back to the table.
8.4.6. Updating Header Styles for Visual Feedback#
To show users the current sort order, we’ll add CSS classes to the column header being sorted.
Add this code:
// Update header styles to indicate sort order table.querySelectorAll("th").forEach(th => th.classList.remove("th-sort-asc", "th-sort-desc")); table.querySelector(`th:nth-child(${column + 1})`).classList.toggle("th-sort-asc", asc); table.querySelector(`th:nth-child(${column + 1})`).classList.toggle("th-sort-desc", !asc);
Explanation:
We first remove any sorting classes (
th-sort-asc
andth-sort-desc
) from all headers to reset them.Then, we add the appropriate class to the sorted column header (
th-sort-asc
for ascending,th-sort-desc
for descending) usingtoggle
.
8.4.7. Adding Click Event Listeners to the Headers#
Finally, we need to set up event listeners on the table headers to trigger sorting when clicked.
document.querySelectorAll(".table-sortable th").forEach(headerCell => { headerCell.addEventListener("click", () => { const tableElement = headerCell.parentElement.parentElement.parentElement; const headerIndex = Array.prototype.indexOf.call(headerCell.parentElement.children, headerCell); const currentIsAscending = headerCell.classList.contains("th-sort-asc"); sortTableByColumn(tableElement, headerIndex, !currentIsAscending); }); });
Explanation:
This code attaches a click event to each header (
<th>
) in.table-sortable
.When a header is clicked, it calculates which column index (
headerIndex
) was clicked.It checks if the column is currently sorted in ascending order and toggles the sort order each time it’s clicked.
sortTableByColumn
is then called with the selected column index and the new sort order.
8.4.8. Final Code#
After implementing each step, your final JavaScript code should look like this:
function sortTableByColumn(table, column, asc = true) {
const dirModifier = asc ? 1 : -1;
const tBody = table.tBodies[0];
const rows = Array.from(tBody.querySelectorAll("tr"));
const sortedRows = rows.sort((a, b) => {
const aColText = a.querySelector(`td:nth-child(${column + 1})`).textContent.trim();
const bColText = b.querySelector(`td:nth-child(${column + 1})`).textContent.trim();
return aColText > bColText ? (1 * dirModifier) : (-1 * dirModifier);
});
while (tBody.firstChild) {
tBody.removeChild(tBody.firstChild);
}
tBody.append(...sortedRows);
table.querySelectorAll("th").forEach(th => th.classList.remove("th-sort-asc", "th-sort-desc"));
table.querySelector(`th:nth-child(${column + 1})`).classList.toggle("th-sort-asc", asc);
table.querySelector(`th:nth-child(${column + 1})`).classList.toggle("th-sort-desc", !asc);
}
document.querySelectorAll(".table-sortable th").forEach(headerCell => {
headerCell.addEventListener("click", () => {
const tableElement = headerCell.parentElement.parentElement.parentElement;
const headerIndex = Array.prototype.indexOf.call(headerCell.parentElement.children, headerCell);
const currentIsAscending = headerCell.classList.contains("th-sort-asc");
sortTableByColumn(tableElement, headerIndex, !currentIsAscending);
});
});