Add column sort to html table – JavaScript – SitePoint Forums
I used a js script to convert JSON data to an HTML table at https://communitychessclub.com/result.php.
convert json to table:
It worked fine. But now I have to add a class = "order"
to table thead tr th (using JS) to sort the columns… How can I do that in plain js?
I have this: <th>Country</th>
But I need this: <th class="order">Country</th>
<script>
//Another compact but readable solution: It just requires adding the class .order to the <th> element of each column to be ordered
document.querySelectorAll('th.order').forEach(th_elem => {
let asc=true
const index = Array.from(th_elem.parentNode.children).indexOf(th_elem)
th_elem.addEventListener('click', (e) => {
const arr = [... th_elem.closest("table").querySelectorAll('tbody tr')]
arr.sort( (a, b) => {
const a_val = a.children[index].innerText
const b_val = b.children[index].innerText
return (asc) ? a_val.localeCompare(b_val) : b_val.localeCompare(a_val)
})
arr.forEach(elem => {
th_elem.closest("table").querySelector("tbody").appendChild(elem)
})
asc = !asc
})
})
</script>
Wern’t you using DataTables before?
Anyway…
let theader = document.createElement("th");
=>
let theader = document.createElement("th");
theader.classList.add("order");
Found a much simpler way with sortable.js as shown at:
https://communitychessclub.com/result.php
But the problem is that I want to hide the first column from the user, but still have it accessible in js.
So, how can I hide “YT_id” column, given the sortable.js script?
<script> // JSON DATA
data = [
{"YT_id":'XmU4Xyl00hY', "title":"Don't Look Back", "author":'Boston'},
{"YT_id":'9wxI4KK9ZYo', "title":'Perfect day', "author":'Lou Reed'},
{"YT_id":'XFkzRNyygfk', "title":'Creep', "author":'Radiohead'},
{"YT_id":'RQsRkDobjSs', "title":'Swallow', "author":'United Strings of Europe'},
{"YT_id":'2oT0UAoe0eQ', "title":'Slave to Love', "author":'Roxy Music'}
];
</script>
<script> // sortable.js
const sortable = function(data) {
const table = document.createElement('table');
const thead = document.createElement('thead');
const tbody = document.createElement('tbody');
const currentOrder = {};
// Function to populate the tbody with the data
const _populate = current => {
const row = tbody.insertRow();
row.innerHTML = Object
.keys(current)
.reduce((carry, key) =>
carry + `<td>${current[key]}</td>`,
''
);
};
// Create the thead from the object keys
thead.innerHTML = '<tr>'+
Object
.keys(data[0])
.reduce((carry, current) =>
carry + `<th data-key="${current}">${current}</th>`,
``
) +
'</tr>';
// Add an event listener to sort the data according
// to the clicked th
thead.addEventListener('click', function(event) {
const key = event.target.dataset.key;
const sorted = currentOrder[key]
? data = data.sort((a, b) => a[key] < b[key] ? 1 : -1)
: data = data.sort((a, b) => a[key] > b[key] ? 1 : -1);
currentOrder[key] = !currentOrder[key];
tbody.innerHTML = '';
sorted.forEach(_populate);
});
// Initialise and return the table
data.forEach(_populate);
table.appendChild(thead);
table.appendChild(tbody);
return table;
};
<script>
Also I need to call a function when user clicks on an entire <tbody> <tr>. Plus I have to pass the current <tr> <td>
values to that function.
Probably using CSS to hide all td:nth-of-type(1)
by disabling their visibility.
You’ve got a reference to the row:
and an example of how to attach a click event to an element already in your code:
I shall leave it to you to put the two together.
event.target
inside the function will reference the row. That element has a .children
property, which refers to all the children of that node (which, for a tr, should be all of it’s associated td’s.)
It kinda works …
[https://communitychessclub.com/result.php]
(https://communitychessclub.com/result.php)
<script> var table = sortable(data);
document.getElementById('container').appendChild(table);
var test = document.getElementById('container');
var rows = container.getElementsByTagName('tr');
for (var i = 0; i < rows.length; i++) {if (i == 0) { continue;}
rows[i].addEventListener('click', function() {
// Do something when a row is clicked
S0 = this.cells[0].textContent;
S1 = this.cells[1].textContent;
S2 = this.cells[2].textContent;
//alert (S0); alert (S1); alert (S2);
console.log('YT_id : ' + S0);
console.log('title : ' + S1);
console.log('author : ' + S2);
window.location.href="https://www.youtube.com/watch?v=" + S0;
});
}
</script>
User can click on any youtube video listed in the table, and it will load just fine. But t if the user clicks on the thead th to sort the column, it sorts just fine, but then subsequent clicking on a youtube video listed in the table tr does nothing!
So I need to avoid adding a js listener to the thead tr.
I played around with the for loop, but I couldn’t get in to work properly.
Can you help?