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 “search” 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.
My first idea was to use a front-end library. I love abstractions, and hate reinventing the wheel. I’ve used the DataTables jQuery plug-in before, and thought it might be a good fit. All I had to do was include two CDN file references – one for CSS styles and another for JavaScript functionality. After that, I could select the table by ID and call a single function:
<link href='//cdn.datatables.net/1.13.2/css/jquery.dataTables.min.css' rel='stylesheet' type='text/css'> <script src="//cdn.datatables.net/1.13.2/js/jquery.dataTables.min.js"></script> <script> $(document).ready( function () { $('#dog-table').DataTable(); } ); </script>
This added quick and easily out-of-the-box functionality, with bells and whistles.
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.
Further, the DataTables library would occasionally throw errors about an “incorrect column count”, 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’t strictly semantic and the information wasn’t just tabular.
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’re living in the future, I decided to ask an AI chat-bot called ChatGPT.
I asked “How can I use jQuery to sort an HTML table by a specific column?”
It told me that “You can use the sort
method of JavaScript arrays combined with the map
method to sort an HTML table by a specific column,” and gave me a specific code example!
I was very impressed. Here is the JavaScript code it provided:
<script> $(document).ready(function() { var table = $('#myTable tbody tr').get(); table.sort(function(a, b) { var A = $(a).children('td').eq(1).text(); var B = $(b).children('td').eq(1).text(); if(A < B) { return -1; } if(A > B) { return 1; } return 0; }); $.each(table, function(index, row) { $('#myTable').children('tbody').append(row); }); }); </script>
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.
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.
Eventually, it told me “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”. Below, you can see the full code implementation:
<style> table { border-collapse: collapse; width: 100%; } th, td { text-align: left; padding: 8px; border-bottom: 1px solid #ddd; } tr:nth-child(even) { background-color: #f2f2f2; } th { background-color: #4CAF50; color: white; cursor: pointer; } td:first-child { font-weight: bold; } td:nth-child(3), td:nth-child(4) { text-transform: capitalize; } #search-input { padding: 8px; margin-bottom: 12px; width: 100%; box-sizing: border-box; border: 2px solid #ccc; border-radius: 4px; font-size: 16px; } #search-input:focus { outline: none; border-color: #4CAF50; } </style> <input type="text" id="search-input" placeholder="Search breeds..."> <button>Search</button> <table id="dog-table"> <thead> <tr> <th>Breed</th> <th>Origin</th> <th>Size</th> <th>Temperament</th> </tr> </thead> <tbody> <tr> <td>Labrador Retriever</td> <td>Canada</td> <td>Large</td> <td>Friendly, outgoing, and active</td> </tr> <tr> <td>German Shepherd</td> <td>Germany</td> <td>Large</td> <td>Loyal, confident, and courageous</td> </tr> <tr> <td>Poodle</td> <td>France</td> <td>Small to Large</td> <td>Intelligent, elegant, and proud</td> </tr> <tr> <td>Bulldog</td> <td>England</td> <td>Medium</td> <td>Determined, friendly, and calm</td> </tr> <tr> <td>Beagle</td> <td>England</td> <td>Small to Medium</td> <td>Cheerful, determined, and friendly</td> </tr> </tbody> </table> <script> // Get the table element const table = document.querySelector('table'); // Get the header row and its cells const headerRow = table.querySelector('thead tr'); const headerCells = headerRow.querySelectorAll('th'); // Get the table body and its rows const tableBody = table.querySelector('tbody'); const tableRows = tableBody.querySelectorAll('tr'); // Initialize sort order for each column let sortOrders = Array.from(headerCells).map(() => null); // Attach a click event listener to each header cell headerCells.forEach((headerCell, index) => { headerCell.addEventListener('click', () => { // Extract the column index of the clicked header cell const clickedColumnIndex = index; // Toggle the sort order for the clicked column if (sortOrders[clickedColumnIndex] === 'asc') { sortOrders[clickedColumnIndex] = 'desc'; } else { sortOrders[clickedColumnIndex] = 'asc'; } // Sort the rows based on the values in the clicked column and the sort order const sortedRows = Array.from(tableRows).sort((rowA, rowB) => { const valueA = rowA.cells[clickedColumnIndex].textContent; const valueB = rowB.cells[clickedColumnIndex].textContent; const sortOrder = sortOrders[clickedColumnIndex]; const compareResult = valueA.localeCompare(valueB, undefined, { numeric: true }); return sortOrder === 'asc' ? compareResult : -compareResult; }); // Rebuild the table in the sorted order tableBody.append(...sortedRows); }); }); </script>
Using predictive language models as a coding assistant is very helpful. I can’t wait to see what other uses we find for this technology, especially as it gets better.