Sort attribute for tables

Making data tables dynamically sortable without JavaScript would be a big timesaver for devs and make the implementations consistent across the web for users. Since it’s possible to only want some columns or rows sortable, I propose adding sort as an attribute to th elements.

<table>
  <caption>
    Color names and values
  </caption>
  <tbody>
    <tr>
      <th sort scope="col">Name</th>
      <th sort scope="col">HEX</th>
      <th sort scope="col">HSLa</th>
      <th sort scope="col">RGBa</th>
    </tr>
    <tr>
      <th sort scope="row">Teal</th>
      <td><code>#51F6F6</code></td>
      <td><code>hsla(180, 90%, 64%, 1)</code></td>
      <td><code>rgba(81, 246, 246, 1)</code></td>
    </tr>
    <tr>
      <th sort scope="row">Goldenrod</th>
      <td><code>#F6BC57</code></td>
      <td><code>hsla(38, 90%, 65%, 1)</code></td>
      <td><code>rgba(246, 188, 87, 1)</code></td>
    </tr>
  </tbody>
</table>

To handle sorting different values, sort should accept values that are valid arguments for Intl.Collator.

For accessibility, there should be some standard way of announcing the th elements as “sortable” and stating the sort order if engaged (“ascending”, “descending”) and responding to some keypresses to activate/toggle the sorting.

2 Likes

The values would not necessary match the cell text. Expanding, the comparator has to match the data type. We need to associate the data with comparator, HTML already has input type which can be used as a value for sort. text, number, date, datetime-local, month, etc. Obviously it is not universal and can be extended via API.

<tr>
      <th sort >Name</th>
      <th sort="text">HEX is usual text comparable</th>
      <th sort="date">Date</th>
      <th sort="month">$$</th>
      <th sort="brightness">color <!-- needs overriding by JS --> </th>
    </tr>
    <tr>
      <td > red </td> <!-- text -->
      <td value="#F00"> red </td> <!-- compared as hex string -->
      <td value='2022-12-04T19:01:55.946Z'> Dec 4th, 2022 </td>
      <td value='12'> December </td>

      <!-- note the `value` with `type` --> 
      <td value='rgb(128,128,128)' type="color"> Gray </td>

    </tr>
<script>
    import $ from 'css-chain';
    $('[sort="brightness"]').sort = (c1,c2)=> 
    {   // not actual formula, just a sample
        const brightness = (c) =>  c.r*c.r + c.g*c.g + c.b*c.b; 
        return brightness(c1) >brightness(c2) ? c1==c2? 0:-1;
    }; 
</script>
1 Like

Along with sorting comparator type, additional attribute is needed to reflect the sort-direction:

  • ascending
  • descending
  • none
  • reverse

also the sort-order to define which row/column would be sorted first, second , etc. value is 0-based number, defaults to 0 with incrementing.

If several columns with same order (or none) occur, shall value be incremented or kept same?

Proposals like this with incrementally added functionality are perfect fit for templates concept. Will cross-refer the template proposal once it is written. Current templating in HTML is not capable of such transformation.

Only option is XSLT like in custom-element, would update demo with sortable table from XmlView eventually.

I’m not sure if sort order should be set in the markup. After all, you’ll author the table in the order you want the default to be.

I agree that datetime should be an option for sorting, but other things like numeric order, case sensitivity, and ignoring punctuation are all options available in Intl.Collator. I can’t think of another attribute that takes such a complex JSON-like syntax but it would cover most usual/simple cases.

For more complex or custom sorting, a new sort event (or sortstart and sortend events) could be dispatched when sort is triggered but before the sorting happens so that you could preventDefault() and do your own table manipulation. The event would have data on which th element triggered the sort and if it’s ascending or descending.

@Nick_Gard , W3C proposals should be based on actual needs, not a personal preferences. The list of actually required modifiers resides in the most popular enterprise-grade implementations. Would be handy to create such table and during review expose what is missing. For example, on API side we missed the onSortChange event for table and onChange on sorting header.

I’m not sure if sort order should be set in the markup. After all, you’ll author the table in the order you want the default to be.

Markup is just a reflection of the state. You do not want to make an exception of pairing the attributes and state properties. The Declarative Web Application would assume to have all functional and state aspects reflected in markup and exposed to JS. The integrity of data and initial state resides on markup producer. Developer could make a decision to trigger sorting programmatically on any hydration event but still needs the state expressed in table header. Going further the table data in many cases would be loaded dynamically. There you do need to have sorting parameters after data retrieved and ahead of table body population.

I can’t think of another attribute that takes such a complex JSON-like syntax but it would cover most usual/simple cases.

W3C have made a high bad impact decisions by taking “simplest” instead of sufficiently functional decisions. The MVP functionality as stated can be taken from stats of existing JS solutions. Could you, please, point where “JSON-like” syntax been mentioned?

Any sorting has to be aware of:

  • order
  • direction
  • sorting type
  • data type

It is true on any API( look for OData for example ) and UI( look into existing mature implementations )

new sort event (or sortstart and sortend events)

Given approach is not as good as proposed overriding sort property by a callback. The callback can be used on any stage, from data retrieval to hydration while the sort-start|sort-end event are quite specific. In order to be efficient the callback would take a value and type parameters and does not need the DOM references. I.e. it is a data-aware callback vs DOM events.

I am not sure should both be given in proposal. Callbacks approach has to manipulate DOM , while the callback would not change DOM directly, leaving it to browser implementation.

The JSON-like attribute value I was referencing was my proposed "arguments of Intl.Collator" value. Example:

<th sort="[en, de, es], {numeric: true, ignorePunctuation: true, sensitivity: base}">

There are several approaches to sorting marked-up structures like HTML tables. Generally declarative approaches are preferred, as they can be implemented in multiple languages and contexts and are often amenable to optimizations.

One approach is to add a sort key to each table cell. The keys are always compared (say) as strings using the HTML ASCIIlower collation. This means you can either do the sorting server-side or map values to a surrogate key for sorting when generating the markup.

Another approach is to supply a function at runtime; this can either be a comparison function, as in languages like C, or it could generate a surrogate key for each entry, a string, and the implementation then sorts those. In some cases the surrogate keys can be integers and an O(1) radix sort can be used, but at the cost of slightly more memory to store all the keys.

In any of these cases the values can be updated by JavaScript of course.

If you are sorting people’s names, that’s not something you can necessarily do in JavaScript - you’d need to know which parts of the string were family names, and that Norman St John Stevas sorts as “Saint John Stevas, Norman”. So to sort names you often do need a hidden sort key; since it’s not script-dependent for sorting purposes it can be in an attribute.

This reasoning leads to wanting something like

<td sortkey="Saint John Stevas, Norman">Norman St John Stevas</td>

Of course, the sortkey atribute can also be supplied (and changed) from JavaScript in a browser.

Identifying which columns to sort, and which rows to move, especially in the presence of spans, is tricky, although simply saying that tables with spans outside the headers can’t be sorted is probably an acceptable first cut. So a sortable attribute that can have values of ascending, descending, none or sticky (where none means you can’t click on the heading to sort the column and sticky means the entries in the column don’t move when the table is sorted, e.g. for row numbers) may be sufficient.

It would be nice if any table with a “regular” structure could be sorted by default; it’d also be nice if, like some Braille systems, most user agents allowed transposition of regular tables by default.

Then, finally, one can add a property to a table to indicate a sorting function. Or, can use JavaScript unobtrusively to add or update sortkey attributes behind the scenes.

I like the idea of having sortkey on td elements override the default innerText value used to sort but I think it should be optional. If the value of the cell is what’s usually used for sorting, it would be painful and redundant to make devs write it both as the text node and as the sortkey attribute. But your use case of names is a great example of how such an attribute could enhance sorting declaratively instead of implementing a whole JavaScript override.

The keys are always compared (say) as strings using the HTML ASCIIlower collation.

We wouldn’t be able to rely on this sorting for all human languages. It’s why Intl.Collator exists. I think we should leverage that API or at least the thinking that went into that API to account for the many ways humans expect words to be sorted.

it is a data-aware callback vs DOM events.

@suns I’m envisioning table sorting as a UI event, something the user can do to the table, if it’s enabled by the dev. Sorting and constructing the table server-side or on hydration can be done already with templating mechanisms.

A user-initiated sort action would need at least an event dispatched prior to the DOM manipulation so that devs could call preventDefault() and do their own manipulation. I think an event at the end of the default DOM manipulation (like animationend) would be helpful for triggering code that needs to respond to the newly-sorted table.

Can we agree that declarative-only sufficiently functional approach was an initial intent and is a requirement for html extending proposals?

And that js should have ability to access and override each aspect?

If agreed, than complex json payload vs series of attributes is preferrable as this is html-first approach.

Such approach would allow to tune up tables by templates syntax in raw attributes; validate the attribute values in raw HTML, etc. which is not achievable with JSON-like syntax by classic HTML validation techniques.

1 Like

@liamquin, HTML already has an attribute for the key. It is a value. Since attribute is always a string, it has to be accompanied by either type or keytype. Semantic values of those are matching the input and option tags. See attributes in HTML.