JSFiddle - React, Tailwind, and code Playground
HTML
<table border="1">
<thead>
<tr>
<td>
<strong>#</strong>
<span data-bind="click: sorter('id', true)">asc</span>
<span data-bind="click: sorter('id', false)">desc</span>
</td>
<td>
<strong>First</strong>
<span data-bind="click: sorter('firstName', true)">asc</span>
<span data-bind="click: sorter('firstName', false)">desc</span>
</td>
<td>
<strong>Last</strong>
<span data-bind="click: sorter('lastName', true)">asc</span>
<span data-bind="click: sorter('lastName', false)">desc</span>
</td>
<td>
<strong>Email</strong>
</td>
<td>
<strong>City</strong>
<span data-bind="click: sorter('city', true)">asc</span>
<span data-bind="click: sorter('city', false)">desc</span>
</td>
</tr>
</thead>
<tbody data-bind="foreach: items">
<tr>
<td data-bind="text: id"></td>
<td data-bind="text: firstName"></td>
<td data-bind="text: lastName"></td>
<td data-bind="text: emailAddress"></td>
<td data-bind="text: city"></td>
</tr>
</tbody>
</table>
<p>
Sorters:
<!-- ko foreach: sortersObservable -->
<span data-bind="text: $data[0]"></span>: <span data-bind="text: $data[1]"></span>
<!-- /ko -->
</p>
JavaScript
function Item(id, firstName, lastName, emailAddress, city) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.emailAddress = emailAddress;
this.city = city;
}
function compareValues(ascending, value1, value2) {
value1 = value1 || null;
value2 = value2 || null;
if (value1 === value2) {
return 0;
}
if (value1 === null && value2 !== null) {
return ascending ? -1 : 1;
}
if (value2 !== null && value1 === null) {
return ascending ? 1 : -1;
}
if (typeof value1 === 'number') {
return ascending ? value1 - value2 : value2 - value1;
}
value1 = value1.toString();
value2 = value2.toString();
if (ascending) {
return value1 > value2 ? 1 : value1 < value2 ? -1 : 0;
}
return value1 < value2 ? 1 : value1 > value2 ? -1 : 0;
}
var model = {
items: ko.observableArray(),
sortersObservable: ko.observableArray(),
sorter: function (property, ascending) {
return function () {
var list = model.sorter.list = model.sorter.list || {},
s,
i;
if (list[property] === ascending) {
delete list[property];
} else {
list[property] = ascending;
}
var s = [], i = 0;
for (i in list) {
s.push([i, list[i]]);
}
model.sortersObservable(s);
}
}
};
model.items.push(new Item(1, 'Jane', 'Doe', '[email protected]', 'Los Angeles'));
model.items.push(new Item(2, 'John', 'Doe', '[email protected]', 'Los Angeles'));
model.items.push(new Item(3, 'John', 'Bloggs', '[email protected]', 'San Francisco'));
model.items.push(new Item(4, 'Happy', 'Citizen', '[email protected]', 'San Francisco'));
model.items.push(new Item(5, 'Ham', 'Burger',...