Vanilla Breeze

data-sortable

Client-side list/table sorting upscale on any container — native ul/ol/table or VB collections. Two API shapes (clickable column headers OR external select/button); composes with data-paged.

Overview

The data-sortable attribute upscales any list-shaped container with client-side sorting. Same upscale model as data-paged: pure attribute, no custom element, auto-attaches via the shared init registry. Two API shapes share one engine — clickable <th> headers for tables, external controls (<select> or <button>) for other containers.

Two shapes

Table mode

Clicking <th data-sort="key"> cycles the column through asc → desc → none → asc. Row sort-value comes from the matching <td> via (in order): data-value, <time datetime> when type is date, otherwise textContent.

List mode

Items declare data-sort-{key}=value attributes; an external control declares data-sort-target=selector and supplies the active key. A <select>'s value (or a <button>'s data-sort-by attribute) chooses the key. Leading - on the value flips direction.

Attributes

AttributeDefaultNotes
data-sortableOpt-in (boolean) on the container.
data-sortable-defaultInitial sort key (e.g. -date for newest-first).
data-sort-byReflected on the host: current sort key.
data-sort-directionReflected on the host: asc · desc · none
data-sortOn <th> in table mode: column key.
data-sort-typetexttext · number · date — on container or <th>.
data-sort-targetOn external controls: CSS selector for the target container.
data-sort-byOn a <button> control: the key to apply on click.
data-sort-{key}On list children: per-key sort value.
data-valuePer-cell override; takes precedence over textContent.

Events

The container emits sort:change after each reorder. Use it for analytics, scroll-restoration, or driving a sibling component.

Comparators

  • textlocaleCompare with sensitivity: "base" and numeric: true. Case-insensitive; "item 10" sorts after "item 2".
  • numberNumber() coercion; non-numeric values sort to the end.
  • dateDate.parse. Use ISO 8601 (YYYY-MM-DD) or <time datetime> for unambiguous parsing.

Empty / nullish values always sort to the end regardless of direction — "no value" is treated as "no opinion, put it last."

Accessibility

  • Table headers in table mode get role="button", tabindex="0", and reflected aria-sort (ascending / descending / none).
  • Headers respond to Enter and Space.
  • External controls use native <select> / <button> — native a11y.

Compose with data-paged

Drop both attributes on the same container. Sorting reorders children; the data-paged engine's MutationObserver sees the reorder and re-paginates from page 1, so the user always lands on the first page of the newly sorted list.

When to reach for data-table instead

<data-table> ships its own column-level data-sort alongside data-weight, data-rollup, data-heatmap, etc. If you're already using <data-table> for typed cells / weighted rollups / heatmaps, use its built-in sort. Reach for data-sortable when:

  • You're sorting non-table containers (<ul>, <ol>, <card-list>, <layout-grid>).
  • You're working with a plain <table> and don't want the <data-table> upgrade surface.