Additions to HTML tables that enable sorting.
This specification is an experimental breakup of the HTML specification. You can see the full list on the index page and take part in the discussion in the repository.
The sortable
attribute on
table
elements is a boolean attribute. When present, it indicates that
the user agent is to allow the user to sort the table
.
To make a column sortable in a table
with a thead
, the column needs
to have th
element that does not span multiple
columns in a thead
above any rows that it is to sort.
To make a column sortable in a table
without a thead
, the column
needs to have th
element that does not span multiple
columns in the first tr
element of the table
, where that
tr
element is not in a tfoot
.
When the user selects a column by which to sort, the user agent sets the th
element's sorted
attribute. This attribute can also
be set manually, to indicate that the table should be automatically sorted, even when scripts
modify the page on when the page is loaded.
The sorted
attribute, if specified, must have a value that
is a set of space-separated tokens consisting of optionally a token whose value is an
ASCII case-insensitive match for the string "reversed
", and optionally a token whose value
is a valid non-negative integer greater than zero, in either order.
In other words, ignoring spaces and case, the sorted
attribute's value can be empty, "reversed
", "1
", "reversed 1
", or
"1 reversed
", where "1" is any number equal to or greater than 1.
While one or more th
elements in the table have a sorted
attribute, the user agent will keep the table's data rows
sorted. The value of the attribute controls how the column is used in determining the sort order.
The reversed
keyword means that the column sort
direction is reversed, rather than normal, which is the default if the keyword
is omitted. The number, if present, indicates the column key ordinality; if the number
is omitted, the column is the primary key, as if the value 1 had been specified.
Thus, sorted="1"
indicates the table's primary key, sorted="2"
its secondary key, and so forth.
A sorting-capable th
element is a th
element that matches
all the following conditions simultaneously:
It corresponds to a cell whose width is 1.
(Specifically, a header cell, since this is a th
element.)
There is no cell that corresponds to another
sorting-capable th
element that covers slots in the same column but on a higher (earlier) row.
If the cell's table has
a row group corresponding to a thead
element, the cell is in a row
group that corresponds to the first thead
element of the cell's table.
Otherwise: the cell is not in a row group corresponding to a tfoot
element, and
the cell is in the first row
of the table.
In other words, each column can have one
sorting-capable th
element; this will be the highest th
in
a thead
that spans no other columns, or, if there is no thead
, the
th
in the first row (that is not in a tfoot
), assuming it spans no
columns.
The sorting-capable th
elements of the table
element table are the sorting-capable
th
elements whose cell's table is table.
A table
element table is a sorting-capable
table
element if there are one or more sorting-capable th
elements of the table
element table.
A th
element is a sorting-enabled th
element if it is a
sorting-capable th
element and it has a sorted
attribute.
The sorting-enabled th
elements of the table
element table are the sorting-enabled
th
elements whose cell's table is table.
A table
element table is a sorting-enabled
table
element if there are one or more sorting-capable th
elements of the table
element table, and at least one of
them is a sorting-enabled th
element (i.e. at least one has a sorted
attribute).
A table
element is a table
element with a user-agent exposed
sorting interface if it is a sorting-capable table
element and has
a sortable
attribute specified.
A sorting interface th
element is a sorting-capable
th
element whose cell's table is a table
element with a user-agent exposed
sorting interface.
Each table
element has a currently-sorting flag, which must initially
be false.
The sorted
attribute must not be specified on
th
elements that are not sorting-capable
th
elements. The sortable
attribute
must not be specified on table
elements that are not sorting-capable table
elements.
To determine a th
element's sorted
attribute's
column sort direction and column key ordinality, user agents must use
the following algorithm:
Let direction be normal.
Let have explicit direction be false.
Let ordinality be 1.
Let have explicit ordinality be false.
Let tokens be the result of splitting the attribute's value on spaces.
For each token token in tokens, run the appropriate steps from the following list:
reversed
"Let direction be reversed and have explicit direction be true.
Parse token as an integer. If this resulted in an error or the value zero, then ignore the token. Otherwise, set ordinality to the parsed value, and set have explicit ordinality to true.
Ignore the token.
The column sort direction is the value of direction, and the column key ordinality is the value of ordinality.
A table
must not have two th
elements whose sorted
attribute have the same column key
ordinality.
The table sorting algorithm, which is applied to a table
, is as
follows:
Let table be the table
element being sorted.
If table's currently-sorting flag is true, then abort these steps.
Set table's currently-sorting flag to true.
Fire a simple event named sort
that is
cancelable at table.
If the event fired in the previous step was canceled, then jump to the step labeled end below.
If table is not now a sorting-enabled table
element, then jump to the step labeled end below.
Even if table was a sorting-enabled table
element when the algorithm was invoked, the DOM might have been entirely changed by the
event handlers for the sort
event, so this has to be verified at
this stage, not earlier.
Let key heading cells be the sorting-enabled th
elements of the table
element table.
Sort key heading cells in ascending order of the column key
ordinality of their sorted
attributes, with those
having the same column key ordinality being sorted in tree order.
Let row collection cursor be a pointer to an element, initially pointing
at the first child of table that is after table's first
thead
, if any, and that is either a tbody
or a tr
element, assuming there is one. If there is no such child, then jump to the step labeled
end below.
If table has no row group
corresponding to a thead
element, then set ignore first group to
true. Otherwise, set it to false.
Run these substeps:
Row loop: Let rows be an empty list of tr
elements.
Run these substeps:
Run the appropriate steps from the following list:
tr
element
Collect: Append the element pointed to by row collection cursor to rows.
If there are no tr
or tbody
children of table that are later siblings of the element pointed to by row
collection cursor, or if the next such child is a tbody
element, then jump
to the step labeled group below.
Let row collection cursor point to the next tr
child
of table that is a later sibling of the element pointed to by row collection cursor.
Jump back to the step labeled collect above.
tbody
element
Place all the tr
element children of the element pointed to by row collection cursor into rows, in tree
order.
If rows is empty, jump to the step labeled increment loop below.
Group: Let groups be an empty list of groups of tr
elements.
Let group be an empty group of tr
elements.
Let group cursor be a pointer to an element, initially pointing at the
first tr
element in rows.
Run these substeps:
Start group: Let pending rows in group be 1.
Run these substeps:
Group loop: Append the tr
element pointed to by group
cursor to group.
If there are any cells whose highest row's element is the one pointed to by group cursor, then let tallest height be the number of rows covered by the tallest such cell.
If tallest height is greater than pending rows in group then set pending rows in group to tallest height.
Decrement pending rows in group by one.
Let group cursor point to the next tr
element in rows, if any; otherwise, let it be null.
If group cursor is not null and pending rows in group is not zero, jump back to the step labeled group loop.
Append a new group to groups consisting of the tr
elements in group.
Empty group.
If group cursor is not null, then jump back to the step labeled start group.
If ignore first group is true, then drop the first group in groups and set ignore first group to false.
Run these steps:
Drop leading header groups: If groups is now empty, jump to the step labeled increment loop below.
If the first group of groups consists of tr
elements
whose element children are all th
elements, then drop the first group in groups and jump back to the previous step (labeled drop leading header
groups).
Let insertion point be a placeholder in a DOM tree, which can be used
to reinsert nodes at a specific point in the DOM. Insert insertion point into
the parent of the first tr
element of the first group in groups,
immediately before that tr
element.
Sort the groups in groups, using the following algorithm to decide the relative order of any two groups a and b (the algorithm either returns that a comes before b, or that b comes before a):
Let key index be an index into key heading cells, initially denoting the first element in the list.
Let direction be a sort direction, initially ascending. Its other possible value is descending. When direction is toggled, that means that if its value is ascending, it must be changed to descending, and when its value is descending, it must be changed to ascending.
Run these substeps:
Column loop: Let th be the key indexth
th
in key heading cells.
If th's sorted
attribute's
column sort direction is reversed, then toggle direction.
Let tentative order be the result of comparing two row groups
using the th
element th, with a and
b as the rows.
If tentative order is not "equal", then jump to the step labeled return below.
Increment key index.
If key index still denotes a th
element in key heading cells, then jump back to the step above labeled column
loop.
If a's tr
elements precede b's in
tree order, then let tentative order be "a before b".
Otherwise, let tentative order be "b before a".
Return: Return the relative order given by the matching option from the following list:
When the user agent is required to compare two row groups using the th
element th,
with a and b being the two row groups respectively, the
user agent must run the following steps:
Let x be the x-coordinate of the slots that
th
covers in its table.
Let cella be the element corresponding to the cell in the first row of group a that covers the slot in that row whose x-coordinate is x.
Let cellb be the element corresponding to the cell in the first row of group b that covers the slot in that row whose x-coordinate is x.
In either case, if there's no cell that actually covers the slot, then use the value null instead.
Let typea and valuea be the type and value of the cell cella, as defined below.
Let typeb and valueb be the type and value of the cell cellb, as defined below.
The type and value of the cell cell are computed as follows.
If cell is null, then the type is "string" and the value is the empty string; abort these steps.
If, ignoring inter-element whitespace and nodes other than
Element and Text nodes, cell has only one child
and that child is a data
element, then the value is the value of that
data
element's value
attribute, if there is
one, or the empty string otherwise; the type is "string".
If, ignoring inter-element whitespace and nodes other than
Element and Text nodes, cell has only one child
and that child is a progress
element, then the value is the value of that
progress
element's value
attribute, if
there is one, or the empty string otherwise; the type is "string".
If, ignoring inter-element whitespace and nodes other than
Element and Text nodes, cell has only one child
and that child is a meter
element, then the value is the value of that
meter
element's value
attribute, if there is
one, or the empty string otherwise; the type is "string".
If, ignoring inter-element whitespace and nodes other than
Element and Text nodes, cell has only one
child and that child is a time
element, then the value is the
machine-readable equivalent of the element's contents, if any, and the type is
the kind of value that is thus obtained
(a month,
a date,
a yearless date,
a time,
a local date and time,
a time-zone offset,
a global date and time,
a week,
a year, or
a duration);
abort these steps after completing this one.
If there is no machine-readable equivalent, then the type is "string" and the value is the empty string.
If the type is a month, a date, a week, or a year, then change the value to be the instant in time (with no time zone) that describes the earliest moment that the value represents, and change the type to be a local date and time.
For example, if the cell was <td><time>2011-11</time>
then for sorting purposes the value is
interpreted as "2011-11-01T00:00:00.000" and the type is treated as a local date and time rather than a month.
Similarly, if the cell was <td><time
datetime="2014">MMXIV</time>
then for sorting purposes the value is interpreted as
"2014-01-01T00:00:00.000" and the type is treated as a local date and time rather than a year.
The value is the element's textContent
. The type is "string".
If typea and typeb are not equal, then: return "a before b" if typea is earlier in the following list than typeb, otherwise, return "b before a"; then, abort these steps.
If valuea and valueb are equal, then return "equal" and abort these steps.
If typea and typeb are not "string", then: if valuea is earlier than valueb then return "a before b" and abort these steps, otherwise, return "b before a" and abort these steps.
Values sort in their natural order, with the following additional constraints:
For time values, 00:00:00.000 is the earliest value and 23:59:59.999 is the latest value.
For yearless date values, 01-01 is the earliest value and 12-31 is the latest value; 02-28 is earlier than 02-29 which is earlier than 03-01.
Values that are local date and time compare as if they were in the same time zone.
For time-zone offset values, -23:59 is the earliest value and +23:59 is the latest value.
Let componentsa be the result of parsing the sort key valuea.
Let componentsb be the result of parsing the sort key valueb.
As described below, componentsa and componentsb are tuples consisting of a list of n numbers, a list of n number strings, a list of n+1 non-numeric strings, and a list of 2n+1 raw strings, for any non-negative integer value of n (zero or more).
Let order be the result of a locale-specific string comparison of componentsa's first non-numeric string and componentsb's first non-numeric string, in the context of th.
If order is not "equal" then return order and abort these steps.
If componentsa and componentsb both have exactly one number, then run these substeps:
If componentsa's number is less than componentsb's number, return "a before b".
If componentsb's number is less than componentsa's number, return "b before a".
Let order be the result of a locale-specific string comparison of componentsa's second non-numeric string and componentsb's second non-numeric string, in the context of th.
If order is not "equal" then return order and abort these steps.
Let order be the result of a locale-specific string comparison of componentsa's number string and componentsb's number string, in the context of th.
If order is not "equal" then return order and abort these steps.
Otherwise, run these substeps:
If componentsa has zero numbers but componentsb has more than zero numbers, return "a before b".
If componentsb has zero numbers but componentsa has more than zero numbers, return "b before a".
If componentsa has one number, return "a before b".
If componentsb has one number, return "b before a".
If componentsa and componentsb have more than one number, run these substeps:
Let count be the smaller of the number of numbers in componentsa and the number of numbers in componentsb.
For each number in componentsa and componentsb from the first to the countth, in order: if componentsa's number is less than componentsb's number, then return "a before b" and abort these steps; otherwise, if componentsb's number is less than componentsa's number, return "b before a" and abort these steps.
If componentsa has fewer numbers than componentsb, return "a before b" and abort these steps.
If componentsb has fewer numbers than componentsa, return "b before a" and abort these steps.
Let index be zero.
String loop: Let order be the result of a locale-specific string comparison of componentsa's indexth number string and componentsb's indexth number string, in the context of th.
If order is not "equal" then return order and abort these steps.
Increment index.
Let order be the result of a locale-specific string comparison of componentsa's indexth separator string and componentsb's indexth separator string, in the context of th.
If order is not "equal" then return order and abort these steps.
If index is less than the number of numbers in componentsa and componentsb, jump back to the step labeled string loop.
Let index be zero.
Run these substeps:
Final loop: Let order be the result of a raw string comparison of componentsa's nth raw string and componentsb's nth raw string.
If order is not "equal" then return order and abort these steps.
Increment index.
If index is less than the number of raw strings in componentsa and componentsb, jump back to the step labeled final loop.
Return "equal".
Let new order be a list of tr
elements consisting of the
tr
elements of all the groups in the newly ordered groups, with
the tr
elements being in the same order as the groups to which they belong are in
groups, and the tr
elements within each such group themselves
being ordered in tree order.
Remove all the tr
elements in new order from their parents, in tree order.
Insert all the tr
elements in new order into the DOM at the location of insertion point, in
the order these elements are found in new order.
Remove insertion point from the DOM.
Increment loop: If there are no tr
or tbody
children of
table that are later siblings of the element pointed to by row
collection cursor, then jump to the step labeled end below.
Let row collection cursor point to the next tr
or
tbody
child of table that is a later sibling of the element
pointed to by row collection cursor.
Jump back to the step labeled row loop above.
End: Set table's currently-sorting flag to false.
When a user agent is to parse the sort key value, it must run the following steps. These return a tuple consisting of a list of n numbers, a list of n number strings, a list of n+1 non-numeric strings, and a list of 2n+1 raw strings, respectively, for any non-negative integer value of n (zero or more).
Let raw strings be a list of strings initially containing just one entry, an empty string.
Let negatives prejudiced be false.
Let decimals prejudiced be false.
Let exponents prejudiced be false.
Let buffer be the empty string.
Let index be zero.
Let mode be "separator".
When a subsequent step in this algorithm says to push the buffer, the user agent must run the following substeps:
Let checkpoint buffer be the empty string.
Let checkpoint index be zero.
When a subsequent step in this algorithm says to checkpoint, the user agent must run the following substeps:
Set the checkpoint buffer to the value of buffer.
Set the checkpoint index to the value of index.
When a subsequent step in this algorithm says to push the checkpoint, the user agent must run the following substeps:
Run through the following steps repeatedly until the condition in the last step is met.
Top of loop: If index is equal to or greater than the number of characters in value, let c be EOF. Otherwise, let c be the indexth character in value.
Run the appropriate steps from the following list:
Run the appropriate substeps from the following list:
Set negatives prejudiced to false.
Set decimals prejudiced to false.
Set exponents prejudiced to false.
Append c to the last entry in raw strings.
Set buffer to the value of c.
Set buffer to the value of c.
Set mode to "leading-decimal".
Set buffer to the value of c.
Set exponents prejudiced to true.
Append c to the last entry in raw strings.
Do nothing.
Append c to the last entry in raw strings.
Run the appropriate substeps from the following list:
Set negatives prejudiced to true.
Append buffer to the last entry in raw strings.
Append c to the last entry in raw strings.
Append c to buffer.
Set mode to "leading-decimal".
Append c to buffer.
Append buffer to the last entry in raw strings.
Decrement index by one.
Run the appropriate substeps from the following list:
Set negatives prejudiced to true.
Append c to buffer.
Append c to the last entry in raw strings.
Append c to buffer.
Run the appropriate substeps from the following list:
Append c to buffer.
Append buffer to the last entry in raw strings.
Decrement index by one.
Run the appropriate substeps from the following list:
Set negatives prejudiced to true.
Set decimals prejudiced to true.
Append c to buffer.
Append c to buffer.
Run the appropriate substeps from the following list:
Append c to buffer.
Set mode to "exponent-negative".
Set decimals prejudiced to true.
Append c to buffer.
Set mode to "exponent-number".
Set exponents prejudiced to true.
Run the appropriate substeps from the following list:
Set negatives prejudiced to true.
Set decimals prejudiced to true.
Append c to buffer.
Set mode to "exponent-negative-number".
Set exponents prejudiced to true.
Run the appropriate substeps from the following list:
Set negatives prejudiced to true.
Set decimals prejudiced to true.
Append c to buffer.
Set exponents prejudiced to true.
Run the appropriate substeps from the following list:
Set negatives prejudiced to true.
Set decimals prejudiced to true.
Append c to buffer.
Set exponents prejudiced to true.
Increment index by one.
If index is greater than the number of characters in value, stop repeating these substeps and continue along the overall steps. Otherwise, return to the step labeled top of loop.
Let numbers be an empty list.
Let number strings be an empty list.
Let non-numeric strings be an empty list.
For each even-numbered entry in raw strings, in order, starting from the first entry (numbered 0), append an entry to non-numeric strings that consists of the result of trimming and collapsing the value of the entry.
If raw strings has more than one entry, then, for each odd-numbered entry in raw strings, in order, starting from the second entry (numbered 1), append an entry to number strings that consists of the value of the entry, and append an entry to number strings that consists of the result of parsing the value of the entry using the rules for parsing floating-point number values.
Return numbers, number strings, non-numeric strings, and raw strings respectively.
When the user agent is required by the step above to perform a locale-specific string comparison of two strings a and b in the context of an element e, the user agent must apply the Unicode Collation Algorithm, using the Default Unicode Collation Element Table as customised for the language of the element e in the Common Locale Data Repository, to the strings a and b, ignoring case. If the result of this algorithm places a first, then return "a before b"; if it places b first, then return "b before a"; otherwise, if they compare as equal, then return "equal". [[!UCA]] [[!CLDR]]
When the user agent is required by the step above to perform a raw string comparison of two strings a and b, the user agent must apply the Unicode Collation Algorithm, using the Default Unicode Collation Element Table without customizations, to the strings a and b. If the result of this algorithm places a first, then return "a before b"; if it places b first, then return "b before a"; otherwise, if they compare as equal, then return "equal". [[!UCA]]
Where the steps above refer to trimming and collapsing a string value, it means running the following algorithm:
Strip leading and trailing whitespace from value.
Replace any sequence of one or more space characters in value with a single U+0020 SPACE character.
When any of the descendants of a sorting-enabled table
element change
in any way (including attributes changing), and when a table
element becomes a
sorting-enabled table
element, the table
element is said to
become a table with a pending sort. When a table
element becomes a
table with a pending sort, the user agent must queue a microtask that applies
the table sorting algorithm to that table
, and then flags the
table
as no longer being a table with a pending sort.
When the user agent is to set the sort key to a th
element target, it must run the following algorithm:
Let table be the table
of the table of which target is a header cell.
If th
is a sorting-enabled th
element whose
column key ordinality is 1, then: if its column sort direction is
normal, set that element's sorted
attribute to the
string "reversed
", otherwise, set it to the empty string; then, abort these
steps.
Let current headers be the sorting-enabled th
elements of the table
element table, excluding target.
Sort current headers by their sorted
attributes' column key ordinality, in ascending
order, with elements that have the same column key ordinality being sorted in
tree order.
Let level be 2.
For each th
element th in current
headers, in order, run the following substeps:
If th's sorted
attribute's
column sort direction is normal, then set th's sorted
attribute to a valid integer whose value is
level. Otherwise, set it to the concatenation of the string "reversed
", a U+0020 SPACE character, and a valid integer whose
value is level.
Increment level by 1.
Set target's sorted
attribute to
the empty string.
The activation behaviour of a sorting interface th
element is to set the sort key to the th
element.
The table
will be sorted the next time the user agent performs a microtask checkpoint.
sort
()Act as if the user had indicated that this was to be the new primary sort column.
The table
won't actually be sorted until the script terminates.
stopSorting
()Removes all the sorted
attributes that are causing the
table to automatically sort its contents, if any.
The th
element's sort()
method, when
invoked, must run the following steps:
If the th
element is not a sorting-capable th
element, then abort these steps.
Set the sort key to the th
element.
The table
will be sorted the next time the user agent performs a microtask checkpoint.
The table
element's stopSorting()
method, when invoked, must remove
the sorted
attribute of all the sorting-enabled
th
elements of the table element on which the method was invoked.