tofsjonas / sortable

Vanilla JavaScript table sort
The Unlicense
429 stars 50 forks source link

Accessibility concerns #62

Closed swirtSJW closed 1 year ago

swirtSJW commented 1 year ago

The headers become clickable but are not actual <a> tags so they can not be accessed with keyboard actions. They are only available to mouse clicks.

Suggested behavior

tofsjonas commented 1 year ago

Hm. I guess this is how I would do it:

document.addEventListener("DOMContentLoaded", () => {
  const headers = document.querySelectorAll(".sortable th");

  headers.forEach((header) => {
    const header_element = header;

    header_element.setAttribute("tabindex", "0");

    // Generate aria-label based on header content
    const header_text = header_element.textContent || "";
    const aria_label = `Click to sort table by ${header_text}`;
    header_element.setAttribute("aria-label", aria_label);

    // Attach event handlers
    header_element.addEventListener("keydown", handleKeyDown);
  });
});

function handleKeyDown(event) {
  if (event.key === "Enter") {
    const element = event.target;
    element.click();
  }
}
tofsjonas commented 1 year ago

...or if we go all in:

const enhanceSortableAccessibility = (tables) => {
  function updateAriaLabel(header_element) {
    // Generate aria-label based on header content
    const header_text = header_element.textContent || "";
    const direction = header_element.classList.contains("dir-d")
      ? "ascending"
      : "descending";

    const aria_label = `Click to sort table by ${header_text} in ${direction} order`;

    header_element.setAttribute("aria-label", aria_label);
    header_element.setAttribute("title", aria_label); // so we can easily see what it does
  }
  function handleKeyDown(event) {
    if (event.key === "Enter") {
      const element = event.target;
      element.click();
    }
  }

  tables.forEach((table) => {
    const headers = table.querySelectorAll("th");
    headers.forEach((header) => {
      const header_element = header;

      header_element.setAttribute("tabindex", "0");
      updateAriaLabel(header_element);

      header_element.addEventListener("click", () => {
        // Add a delay to allow the new sort order to be applied
        setTimeout(() => {
          updateAriaLabel(header_element);
        }, 50);
      });

      // Attach event handlers
      header_element.addEventListener("keydown", handleKeyDown);
    });
  });
};

document.addEventListener("DOMContentLoaded", () => {
  const tables = document.querySelectorAll(".sortable");
  enhanceSortableAccessibility(tables);
});
tofsjonas commented 1 year ago

Hm. I think I'll make this into an add-on. Thank you for bringing it up! 🙏

swirtSJW commented 1 year ago

Wow. Amazing response time. Thank you so much for jumping on this so quickly. Well done. Cheers.