{"id":1793,"date":"2022-05-20T18:21:50","date_gmt":"2022-05-20T18:21:50","guid":{"rendered":"https:\/\/www.antpace.com\/blog\/?p=1793"},"modified":"2025-08-25T17:57:30","modified_gmt":"2025-08-25T17:57:30","slug":"sort-an-html-table-using-javascript","status":"publish","type":"post","link":"https:\/\/www.antpace.com\/blog\/sort-an-html-table-using-javascript\/","title":{"rendered":"Sort an HTML Table Using JavaScript"},"content":{"rendered":"<p>For a recent side project I was tasked with enhancing an existing HTML table. That table displayed search results. The records were dynamic, populated by an AJAX call after the &#8220;search&#8221; button was pressed. One of the requests was to let users click on the column headers to sort the table. Each click would organize the data, toggling ascending and descending, based on the column values.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1796\" src=\"https:\/\/www.antpace.com\/blog\/wp-content\/uploads\/2023\/02\/dog-table.png\" alt=\"A table with data about dogs\" width=\"1031\" height=\"331\" \/><\/p>\n<p>My first idea was to use a front-end library. I love abstractions, and hate reinventing the wheel. I&#8217;ve used the <a href=\"https:\/\/datatables.net\/\" target=\"_blank\" rel=\"noopener\">DataTables<\/a> jQuery plug-in before, and thought it might be a good fit. All I had to do was include two CDN file references &#8211; one for CSS styles and another for JavaScript functionality. After that, I could select the table by ID and call a single function:<\/p>\n<pre>&lt;link href='\/\/cdn.datatables.net\/1.13.2\/css\/jquery.dataTables.min.css' rel='stylesheet' type='text\/css'&gt;\n&lt;script src=\"\/\/cdn.datatables.net\/1.13.2\/js\/jquery.dataTables.min.js\"&gt;&lt;\/script&gt;\n\n&lt;script&gt;\n$(document).ready( function () {\n\t$('#dog-table').DataTable();\n} );\n&lt;\/script&gt;\n<\/pre>\n<p>This added quick and easily out-of-the-box functionality, with bells and whistles.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1798\" src=\"https:\/\/www.antpace.com\/blog\/wp-content\/uploads\/2023\/02\/dog-data-table.png\" alt=\"\" width=\"924\" height=\"326\" \/><\/p>\n<p>It seemed great, but the extras that it added, such as pagination and front-end search were unnecessary, and actually got in the way of the design specification. Those might be easy enough to clean up by passing options into the method call, or even with a bit of custom CSS, but still seemed like overkill.<\/p>\n<p>Further, the DataTables library would occasionally throw errors about an &#8220;incorrect column count&#8221;, depending on what results my back-end provided. That was because the data model, and possible front-end actions for this app were more complex that you might think. The HTML wasn&#8217;t strictly semantic and the information wasn&#8217;t just tabular.<\/p>\n<p>The more I thought about it, the more I felt that plain vanilla JavaScript should be enough to get the job done. And, since we&#8217;re living in the future, I decided to ask an AI chat-bot called ChatGPT.<\/p>\n<p>I asked &#8220;How can I use jQuery to sort an HTML table by a specific column?&#8221;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1799\" src=\"https:\/\/www.antpace.com\/blog\/wp-content\/uploads\/2023\/02\/chat-gpt.png\" alt=\"Asking ChatGPT about coding\" width=\"897\" height=\"545\" \/><\/p>\n<p>It told me that &#8220;You can use the <code>sort<\/code> method of JavaScript arrays combined with the <code>map<\/code> method to sort an HTML table by a specific column,&#8221; and gave me a specific code example!<\/p>\n<p>I was very impressed. Here is the JavaScript code it provided:<\/p>\n<pre>&lt;script&gt;\n$(document).ready(function() {\n  var table = $('#myTable tbody tr').get();\n\n  table.sort(function(a, b) {\n    var A = $(a).children('td').eq(1).text();\n    var B = $(b).children('td').eq(1).text();\n\n    if(A &lt; B) {\n      return -1;\n    }\n\n    if(A &gt; B) {\n      return 1;\n    }\n\n    return 0;\n  });\n\n  $.each(table, function(index, row) {\n    $('#myTable').children('tbody').append(row);\n  });\n});\n&lt;\/script&gt;\n<\/pre>\n<p>I added this code to a click-handler in my app, after adjusting the element selectors. Although it worked (kind of), it did not operate quite as I expected. It only performed the sort on a single column, and did not alternate the order on each click.<\/p>\n<p>I continued to ask the chat-bot more questions, making refinements to the functionality. I wanted the code to toggle between ascending and descending on each click. Also, I figured it could be nice to avoid jQuery completely and just use basic JS.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1801\" src=\"https:\/\/www.antpace.com\/blog\/wp-content\/uploads\/2023\/02\/chat-bot.png\" alt=\"Chat bot solving code problems\" width=\"869\" height=\"482\" \/><\/p>\n<p>Eventually, it told me &#8220;To toggle between ascending and descending order when sorting the table, you can keep track of the current sorting order for each column in a separate array&#8221;. Below, you can see the full code implementation:<\/p>\n<pre>&lt;style&gt;\n  table {\n  border-collapse: collapse;\n  width: 100%;\n}\n\nth, td {\n  text-align: left;\n  padding: 8px;\n  border-bottom: 1px solid #ddd;\n}\n\ntr:nth-child(even) {\n  background-color: #f2f2f2;\n}\n\nth {\n  background-color: #4CAF50;\n  color: white;\n  cursor: pointer;\n}\n\ntd:first-child {\n  font-weight: bold;\n}\n\ntd:nth-child(3), td:nth-child(4) {\n  text-transform: capitalize;\n}\n#search-input {\n  padding: 8px;\n  margin-bottom: 12px;\n  width: 100%;\n  box-sizing: border-box;\n  border: 2px solid #ccc;\n  border-radius: 4px;\n  font-size: 16px;\n}\n\n#search-input:focus {\n  outline: none;\n  border-color: #4CAF50;\n}\n&lt;\/style&gt;\n&lt;input type=\"text\" id=\"search-input\" placeholder=\"Search breeds...\"&gt;\n&lt;button&gt;Search&lt;\/button&gt;\n&lt;table id=\"dog-table\"&gt;\n  &lt;thead&gt;\n    &lt;tr&gt;\n      &lt;th&gt;Breed&lt;\/th&gt;\n      &lt;th&gt;Origin&lt;\/th&gt;\n      &lt;th&gt;Size&lt;\/th&gt;\n      &lt;th&gt;Temperament&lt;\/th&gt;\n    &lt;\/tr&gt;\n  &lt;\/thead&gt;\n  &lt;tbody&gt;\n    &lt;tr&gt;\n      &lt;td&gt;Labrador Retriever&lt;\/td&gt;\n      &lt;td&gt;Canada&lt;\/td&gt;\n      &lt;td&gt;Large&lt;\/td&gt;\n      &lt;td&gt;Friendly, outgoing, and active&lt;\/td&gt;\n    &lt;\/tr&gt;\n    &lt;tr&gt;\n      &lt;td&gt;German Shepherd&lt;\/td&gt;\n      &lt;td&gt;Germany&lt;\/td&gt;\n      &lt;td&gt;Large&lt;\/td&gt;\n      &lt;td&gt;Loyal, confident, and courageous&lt;\/td&gt;\n    &lt;\/tr&gt;\n    &lt;tr&gt;\n      &lt;td&gt;Poodle&lt;\/td&gt;\n      &lt;td&gt;France&lt;\/td&gt;\n      &lt;td&gt;Small to Large&lt;\/td&gt;\n      &lt;td&gt;Intelligent, elegant, and proud&lt;\/td&gt;\n    &lt;\/tr&gt;\n    &lt;tr&gt;\n      &lt;td&gt;Bulldog&lt;\/td&gt;\n      &lt;td&gt;England&lt;\/td&gt;\n      &lt;td&gt;Medium&lt;\/td&gt;\n      &lt;td&gt;Determined, friendly, and calm&lt;\/td&gt;\n    &lt;\/tr&gt;\n    &lt;tr&gt;\n      &lt;td&gt;Beagle&lt;\/td&gt;\n      &lt;td&gt;England&lt;\/td&gt;\n      &lt;td&gt;Small to Medium&lt;\/td&gt;\n      &lt;td&gt;Cheerful, determined, and friendly&lt;\/td&gt;\n    &lt;\/tr&gt;\n  &lt;\/tbody&gt;\n&lt;\/table&gt;\n\n\n&lt;script&gt;\n\/\/ Get the table element\nconst table = document.querySelector('table');\n\n\/\/ Get the header row and its cells\nconst headerRow = table.querySelector('thead tr');\nconst headerCells = headerRow.querySelectorAll('th');\n\n\/\/ Get the table body and its rows\nconst tableBody = table.querySelector('tbody');\nconst tableRows = tableBody.querySelectorAll('tr');\n\n\/\/ Initialize sort order for each column\nlet sortOrders = Array.from(headerCells).map(() =&gt; null);\n\n\/\/ Attach a click event listener to each header cell\nheaderCells.forEach((headerCell, index) =&gt; {\n  headerCell.addEventListener('click', () =&gt; {\n    \/\/ Extract the column index of the clicked header cell\n    const clickedColumnIndex = index;\n\n    \/\/ Toggle the sort order for the clicked column\n    if (sortOrders[clickedColumnIndex] === 'asc') {\n      sortOrders[clickedColumnIndex] = 'desc';\n    } else {\n      sortOrders[clickedColumnIndex] = 'asc';\n    }\n\n    \/\/ Sort the rows based on the values in the clicked column and the sort order\n    const sortedRows = Array.from(tableRows).sort((rowA, rowB) =&gt; {\n      const valueA = rowA.cells[clickedColumnIndex].textContent;\n      const valueB = rowB.cells[clickedColumnIndex].textContent;\n      const sortOrder = sortOrders[clickedColumnIndex];\n      const compareResult = valueA.localeCompare(valueB, undefined, { numeric: true });\n      return sortOrder === 'asc' ? compareResult : -compareResult;\n    });\n\n    \/\/ Rebuild the table in the sorted order\n    tableBody.append(...sortedRows);\n  });\n});\n\n&lt;\/script&gt;\n<\/pre>\n<p>Using predictive language models as a coding assistant is very helpful. I can&#8217;t wait to see what other uses we find for this technology, especially as it gets better.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>For a recent side project I was tasked with enhancing an existing HTML table. That table displayed search results. The records were dynamic, populated by an AJAX call after the &#8220;search&#8221; button was pressed. One of the requests was to let users click on the column headers to sort the table. Each click would organize &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/www.antpace.com\/blog\/sort-an-html-table-using-javascript\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Sort an HTML Table Using JavaScript&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":3243,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[33,66,71,72],"class_list":["post-1793","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-web-development","tag-css","tag-html","tag-javascript","tag-jquery"],"_links":{"self":[{"href":"https:\/\/www.antpace.com\/blog\/wp-json\/wp\/v2\/posts\/1793","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.antpace.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.antpace.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.antpace.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.antpace.com\/blog\/wp-json\/wp\/v2\/comments?post=1793"}],"version-history":[{"count":1,"href":"https:\/\/www.antpace.com\/blog\/wp-json\/wp\/v2\/posts\/1793\/revisions"}],"predecessor-version":[{"id":3244,"href":"https:\/\/www.antpace.com\/blog\/wp-json\/wp\/v2\/posts\/1793\/revisions\/3244"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.antpace.com\/blog\/wp-json\/wp\/v2\/media\/3243"}],"wp:attachment":[{"href":"https:\/\/www.antpace.com\/blog\/wp-json\/wp\/v2\/media?parent=1793"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.antpace.com\/blog\/wp-json\/wp\/v2\/categories?post=1793"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.antpace.com\/blog\/wp-json\/wp\/v2\/tags?post=1793"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}