How We Made Huge Tables Sortable and Accessible


Posted by justin


During a recent SiteImprove crawl, it was brought to our attention that our client’s gigantic tables (~5,000 rows, ~50 columns) were getting docked for accessibility concerns. The tables themselves were just fine, but the jQuery plugin we were using to make them sortable and scrollable manipulated the tables in a very non-accessible fashion. Specifically:

  1. Duplicate IDs
  2. Tables with <thead> cells, but no <tbody> cells
  3. Table cells without header context

The jQuery plugin duplicates the table’s <thead> section to act as a fixed navigation bar that can remain sticky to the top of the viewport. It also hides the original table’s header from view, but not from the DOM. To most users, the table looks the same, but with added features of column sorting and better scrolling. To an accessibility crawler, however, these are no-nos.


Screen readers require table cells (<td>) in a column to be linked to a table header (<th>) within the same <table> element. We tried to just link the now-orphaned cells to the duplicated <thead> section’s <th> cells, but the crawler was on to us and insisted they be contained within the same table. The jQuery plugin didn’t have any updates to resolve this, so we set out to either patch the plugin or find a suitable replacement.

Some effort was made to make the plugin accessible – after all, it does its job. But the more we dug in, the more we realized we’d be rewriting the entire plugin. 

Enter jQuery.FancyTable!

FancyTable offers the same sorting functionality as the original plugin. It also does it without duplicating any table elements — just adding links to the headers for sorting. While FancyTable does not offer an option to make the table headers sticky, some basic CSS resolved the issue: thead { position: sticky; } – really, that’s it. 


We did have to adjust the FancyTable sorting script to account for currency, but it handled dates, times, alphabetical, and numerical sorts out of the box. Which is more than the old plugin could do without customization – our script length and number of network calls went down while working on this update.

We’ve implemented the new solution, and are now clearing all accessibility checks. 🎉🎉

Return to Articles & Guides