I have the html table which populated the dynamic values from json(written in the javascript code).
When user click on any column header in the table, the table should sort accordingly. Any suggestions would be helpful.
When user click on the column the table should sort ascending and then descending if user clicks for the second time on the same column.
Demo https://jsfiddle.net/o2ram4tu/
Sample code below:
function CreateTableFromJSON() {
var myBooks = [
{
"Book ID": "1",
"Book Name": "Computer Architecture",
"Category": "Computers",
"Price": "125.60"
},
{
"Book ID": "2",
"Book Name": "Asp.Net 4 Blue Book",
"Category": "Programming",
"Price": "56.00"
},
{
"Book ID": "3",
"Book Name": "Popular Science",
"Category": "Science",
"Price": "210.40"
}
]
// EXTRACT VALUE FOR HTML HEADER.
// ('Book ID', 'Book Name', 'Category' and 'Price')
var col = [];
for (var i = 0; i < myBooks.length; i++) {
for (var key in myBooks[i]) {
if (col.indexOf(key) === -1) {
col.push(key);
}
}
}
var table = document.getElementById("resulttable");
var tr = table.insertRow(1);
// ADD JSON DATA TO THE TABLE AS ROWS.
for (var i = 0; i < myBooks.length; i++) {
tr = table.insertRow(-1);
for (var j = 0; j < col.length; j++) {
var tabCell = tr.insertCell(-1);
tabCell.innerHTML = myBooks[i][col[j]];
}
}
// FINALLY ADD THE NEWLY CREATED TABLE WITH JSON DATA TO A CONTAINER.
var divContainer = document.getElementById("showData");
divContainer.innerHTML = "";
divContainer.appendChild(table);
}
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<title>Convert JSON Data to HTML Table</title>
<style>
th, td, p, input {
font:14px Verdana;
}
table, th, td
{
border: solid 1px #DDD;
border-collapse: collapse;
padding: 2px 3px;
text-align: center;
}
th {
font-weight:bold;
}
</style>
</head>
<body onload="CreateTableFromJSON()" >
<table class="fdt-datatable" id="resulttable" name="resulttable">
<tbody>
<tr>
<th name = "bookID"> Book ID</th>
<th name = "bookName"> Book Name</th>
<th name = "category"> Category</th>
<th name = "price"> Price</th>
</tr>
</tbody>
</table>
<p id="showData"></p>
</body>
</html>
I edited your code and add some new staff fort sortering
Here is jsfiddle
Snippet is below
function CreateTableFromJSON() {
var myBooks = [
{
"Book ID": "1",
"Book Name": "Computer Architecture",
"Category": "Computers",
"Price": "125.60"
},
{
"Book ID": "2",
"Book Name": "Asp.Net 4 Blue Book",
"Category": "Programming",
"Price": "56.00"
},
{
"Book ID": "3",
"Book Name": "Popular Science",
"Category": "Science",
"Price": "210.40"
}
]
// EXTRACT VALUE FOR HTML HEADER.
// ('Book ID', 'Book Name', 'Category' and 'Price')
var col = [];
for (var i = 0; i < myBooks.length; i++) {
for (var key in myBooks[i]) {
if (col.indexOf(key) === -1) {
col.push(key);
}
}
}
var table = document.getElementById("resulttable");
var tbody = document.getElementById("resulttable_body");
var tr = tbody.insertRow(0);
// ADD JSON DATA TO THE TABLE AS ROWS.
for (var i = 0; i < myBooks.length; i++) {
tr = tbody.insertRow(-1);
for (var j = 0; j < col.length; j++) {
var tabCell = tr.insertCell(-1);
tabCell.innerHTML = myBooks[i][col[j]];
}
}
// FINALLY ADD THE NEWLY CREATED TABLE WITH JSON DATA TO A CONTAINER.
var divContainer = document.getElementById("showData");
divContainer.innerHTML = "";
divContainer.appendChild(table);
}
// FOR TABLE SORT
$(document).ready(function(){
var sortOrder = 1; // flag to toggle the sorting order
function getVal(elm, colIndex){
var td = $(elm).children('td').eq(colIndex).text();
if(typeof td !== "undefined"){
var v = td.toUpperCase();
if($.isNumeric(v)){
v = parseInt(v,10);
}
return v;
}
}
$(document).on('click', '.sortable', function(){
var self = $(this);
var colIndex = self.prevAll().length;
var o = (sortOrder == 1) ? 'asc' : 'desc'; // you can use for css to show sort direction
sortOrder *= -1; // toggle the sorting order
$('.sortable').removeClass('asc').removeClass('desc');
self.addClass(o);
var tbody = self.closest("table").find("tbody");
var tr = tbody.children("tr"); //.get();
tr.sort(function(a, b) {
var A = getVal(a, colIndex);
var B = getVal(b, colIndex);
if(A < B) {
return -1*sortOrder;
}
if(A > B) {
return 1*sortOrder;
}
return 0;
});
$.each(tr, function(index, row) {
//console.dir(row)
tbody.append(row);
});
});
});
th, td, p, input {
font:14px Verdana;
}
table, th, td
{
border: solid 1px #DDD;
border-collapse: collapse;
padding: 2px 3px;
text-align: center;
}
th {
font-weight:bold;
}
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<title>Convert JSON Data to HTML Table</title>
</head>
<body onload="CreateTableFromJSON()" >
<table class="fdt-datatable" id="resulttable" name="resulttable">
<thead>
<tr>
<th name = "bookID" class="sortable"> Book ID</th>
<th name = "bookName" class="sortable"> Book Name</th>
<th name = "category" class="sortable"> Category</th>
<th name = "price" class="sortable"> Price</th>
</tr>
</thead>
<tbody id="resulttable_body">
</tbody>
</table>
<p id="showData"></p>
</body>
</html>
For your question from coment about this jsfiddle code
HTML part
You have table with everything in tbody. I separated row with th to thead and I have empty tbody. Because I need to separate header fields which is clickable, and to separate content which can be sort.
Important thing is to add class sortable on which column you want to be sortable. In this case all column is sortable except first with checkboxes
JavaScript part
I added var tbody = document.getElementById("resulttable_body"); and at the end all rows are added to our empty tbody.appendChild(row);
Mayor thing is that you created element input and you append input to table cell, which look like this image:
I've created td element and put input inside it
//Multi Stage Checkbox creation
tblCell = document.createElement('td');
var input = document.createElement("INPUT");
input.setAttribute("type", "checkbox");
input.setAttribute("name", "");
input.setAttribute("value", "");
input.setAttribute("id", 1);
tblCell.appendChild(input);
tblCell.style.textAlign = 'center';
row.appendChild(tblCell);
Related
I have table that need to custom with my JS in loop.
Below is the demo. What I need the final result is like this:
Is there any trick how to achieve as my result needed?
var jsonStr = {
"data": [
{
"data2": [
{
"partNo": "ABC",
"plan": "120"
},
{
"partNo": "DEF",
"plan": "50"
}
],
"lineID": "1"
},
{
"data2": [
{
"partNo": "FAB",
"plan": "75"
}
],
"lineID": "2"
}
]
};
for(var i=0; i<jsonStr.data.length; i++) {
var line = "LINE " + jsonStr.data[i].lineID;
var element = `<tr><td>${line}</td></tr>`;
$(".tbl1 tbody").append(element);
for(var j=0; j<jsonStr.data[i].data2.length; j++) {
var partNo = jsonStr.data[i].data2[j].partNo;
//console.log(partNo);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table class="tbl1" border="1">
<thead>
<th>Line</th>
<th>Part No.</th>
</thead>
<tbody></tbody>
</table>
<p>
It should be like this:
<table border="1">
<thead>
<th>Line</th>
<th>Part No.</th>
</thead>
<tbody>
<tr>
<td rowspan="2">LINE 1</td>
<td>ABC</td><tr>
<td>DEF</td>
</tr>
<tr>
<td rowspan="1">LINE 2</td>
<td>FAB</td>
</tr>
</tbody>
</table>
This is an approach using Array.forEach over the obj.data property to feed a target table tbody using the rowspan strategy.
Each time a new entry is visited in the new array, a new row is created and is given a rowspan value equal to the number of elements in its own data2 property array (partNo).
Then for each pf those, a new row is added, starting from the second one, holding the current partNo alone.
I didn't see you were using jQuery so I went for vanilla js. Anyway this is the MDN reference to the topics faced here:
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/td#attr-rowspan
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
var obj = {
"data": [
{
"lineID": "1",
"data2": [{"partNo": "ABC","plan": "120"},{"partNo": "DEF","plan": "50"}],
},
{
"lineID": "2",
"data2": [{"partNo": "FAB","plan": "75"}],
}
]
};
//target tbody
const tbody = document.querySelector('#tbl1 tbody');
//for each entry in obj.data
obj.data.forEach( entry => {
//create a new currentRow
let currentRow = document.createElement('tr');
//create a cell for the current line and append it to the currentRow
const tdLine = document.createElement('td');
tdLine.textContent = `LINE ${entry.lineID}`;
if(entry.data2.length > 1)
tdLine.setAttribute('rowspan', entry.data2.length);
currentRow.append(tdLine);
//for each partNo
entry.data2.forEach( (part, i) => {
//if the index of the current partNo is > 0, commit the currentRow and make a new one
if(i > 0){
tbody.append(currentRow);
currentRow = document.createElement('tr');
}
//create the cell for the current partNo and append it to the currentRow
const tdPart = document.createElement('td');
tdPart.textContent = part.partNo;
currentRow.append(tdPart);
});
//append the currentRow to the table
tbody.append(currentRow);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id="tbl1" class="tbl1" border="1">
<thead>
<th>Line</th>
<th>Part No.</th>
</thead>
<tbody></tbody>
</table>
Just set rowspan for the first cell of first group based on data2 length. Then render rest of group's rows with single cell with data2.
const jsonStr = {
"data": [
{
"data2": [
{
"partNo": "ABC",
"plan": "120"
},
{
"partNo": "DEF",
"plan": "50"
}
],
"lineID": "1"
},
{
"data2": [
{
"partNo": "FAB",
"plan": "75"
}
],
"lineID": "2"
}
]
};
const tbody = $(".tbl1").find("tbody");
for(let i=0; i<jsonStr.data.length; i++) {
const label = "LINE " + jsonStr.data[i].lineID;
let tr = document.createElement("tr");
tbody.append(tr);
let td = document.createElement("td");
td.innerHTML = label;
td.rowSpan = jsonStr.data[i].data2.length;
tr.appendChild(td);
td = document.createElement("td");
td.innerHTML = jsonStr.data[i].data2[0].partNo;
tr.appendChild(td);
for(let j=1; j<jsonStr.data[i].data2.length; j++) {
const partNo = jsonStr.data[i].data2[j].partNo;
tr = document.createElement("tr");
tbody.append(tr);
td = document.createElement("td");
td.innerHTML = partNo;
tr.appendChild(td);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table class="tbl1">
<tbody></tbody>
</table>
I have a long json that i want to be displayed only four when the page loads and shows the rest when a user clicks on the investment link.
This is my investment table
<table class="table table-bordered table-striped table-vcenter js-dataTable-full-pagination table-responsive" id="investmentTable">
<thead id="tableHead">
<tr id="tableRow">
<th class="text-center" style="width: 30%;" id="serialNo">S/N</th>
<th class="d-sm-table-cell" style="width: 30%;" id="investmentNo">Investment No</th>
<th class="d-sm-table-cell" style="width: 30%;" id="amount">Amount</th>
<th class="d-sm-table-cell" style="width: 30%;" id="status">Status</th>
</tr>
</thead>
</table>
This is the Json test and code
// Call for Investment Api
function investmentData(){
var myInvestment = [
{
"investmentNo":"00032",
"amount":"70000",
"status": "Expired",
"duration": "2",
"startDate": "7-02-2020",
"yield": "2.60",
"repayAmt":"70500",
"description": "Official"
},
{
"investmentNo":"00033",
"amount":"40000",
"status": "Current",
"duration": "3",
"startDate": "4-01-2019",
"yield": "12.0",
"repayAmt":"42000",
"description": "Personal"
},
{
"investmentNo":"00034",
"amount":"5000",
"status": "Current",
"duration": "4",
"startDate": "5-04-2008",
"yield": "20.0",
"repayAmt":"6000",
"description": "School fees"
}
]
var investmentTable = document.querySelector("#investmentTable");
if(myInvestment.length>0){
var col = []; // define an empty array
for (var i = 0; i < myInvestment.length; i++) {
for (var key in myInvestment[i]) {
if (col.indexOf(key) === -1) {
col.push(key);
}
}
}
// CREATE TABLE HEAD .
var tHead = document.querySelector("#tableHead");
// CREATE ROW FOR TABLE HEAD .
var hRow = document.querySelector("#tableRow");
// ADD COLUMN HEADER TO ROW OF TABLE HEAD.
tHead.appendChild(hRow);
investmentTable.appendChild(tHead);
// CREATE TABLE BODY .
var tBody = document.createElement("tbody");
// ADD COLUMN HEADER TO ROW OF TABLE HEAD.
for (var i = 0; i < myInvestment.length; i++) {
var bRow = document.createElement("tr");
// CREATE ROW FOR EACH RECORD .
var td = document.createElement("td");
td.innerHTML = i+1;
bRow.appendChild(td);
for (var j = 0; j < 3; j++) {
var td = document.createElement("td");
if (j==0) {
td.innerHTML = ''+myInvestment[i][col[j]]+ '';
bRow.appendChild(td);
}else{
td.innerHTML = myInvestment[i][col[j]];
bRow.appendChild(td);
}if (j==2) {
td.innerHTML = '<div class="badge">'+myInvestment[i][col[j]]+ '</div>';
if (td.textContent=="Expired") {
td.innerHTML = '<div class="badge badge-success">'+myInvestment[i][col[j]]+ '</div>';
} else {
td.innerHTML = '<div class="badge badge-danger">'+myInvestment[i][col[j]]+ '</div>';
}
}
}
tBody.appendChild(bRow)
}
investmentTable.appendChild(tBody);
var link = document.getElementsByTagName('a');
for(x=0;x<link.length;x++){
link[x].onclick = function invModalView(k){
var href = this.getAttribute("href");
var modal = document.getElementById("modal-block-normal");
modal.style.display = "block";
var investNo = document.getElementById("investNo");
var investmentTableModal = document.querySelector("#investmentTableModal");
if(myInvestment.length>0){
var col = []; // define an empty array
for (var i = 0; i < myInvestment.length; i++) {
for (var key in myInvestment[i]) {
if (col.indexOf(key) === -1) {
col.push(key);
}
}
}
// CREATE TABLE BODY .
var tBody = document.createElement("tbody");
// ADD COLUMN HEADER TO ROW OF TABLE HEAD.
for (var k = 3; k < 7; k++){
var bRow = document.createElement("tr");
for (var i = 0; i < myInvestment.length; i++) {
// CREATE ROW FOR EACH RECORD .
var td = document.createElement("td");
td.innerHTML = myInvestment[i][col[k]];
bRow.appendChild(td);
}
tBody.appendChild(bRow)
}
investmentTableModal.appendChild(tBody);
}
};
}
}
}
**This is the modal table**
<table class="table table-bordered table-striped table-vcenter table-responsive" id="investmentTableModal">
<thead id="tableHeadModal">
<tr>
<th class="d-sm-table-cell" style="width: 40%;">Investment No</th>
</tr>
<tr>
<th class="d-sm-table-cell" style="width: 35%;">Duration</th>
</tr>
<tr>
<th class="d-sm-table-cell" style="width: 40%;">StartDate</th>
</tr>
<tr>
<th class="d-sm-table-cell" style="width: 40%;">Yield</th>
</tr>
<tr>
<th class="d-sm-table-cell" style="width: 40%;">RepaymentAmount</th>
</tr>
<tr>
<th class="d-sm-table-cell" style="width: 40%;">Description</th>
</tr>
</thead>
</table>
The image is where the problem lies. it displays all values at once when i only want it to display the first array from index 3-7 in a row when a user clicks on the first link.
Each time I input another football score, the league table is updated and displayed but it's appended to a list of tables. How do I display only the latest table?
Here is an extract of the html:
<div>
<table id="matches" border="1"> </table>
</div>
<div>
<table id="standings" border="1"> </table>
</div>
<input type="button" value="Update" onclick="update()" />
Here is the javascript that displays the fixtures for inputting scores:
// Display fixtures to input the scores
window.onload = function()
{
table = document.getElementById("matches");
var row;
var cell1;
var cell2;
var cell3;
for (i = 1; i < Results.length; i++)
{
row = table.insertRow(i-1); //table starts row 0 but Results row 1 so i-1 used
cell1 = row.insertCell(0);
cell2 = row.insertCell(1);
cell3 = row.insertCell(2);
cell4 = row.insertCell(3);
cell1.innerHTML = Results[i][0];
cell2.innerHTML = '<input type="number" min="0" max="99"/>'
cell3.innerHTML = '<input type="number" min="0" max="99"/>'
cell4.innerHTML = Results[i][3];
}
}
And here is the code that displays the table after the lastest scores have been inputed:
// Display League Table
standings = document.getElementById("standings");
for (i = 0; i < League.length; i++)
{
row = standings.insertRow(i);
cell1 = row.insertCell(0);
cell2 = row.insertCell(1);
cell3 = row.insertCell(2);
cell4 = row.insertCell(3);
cell5 = row.insertCell(4);
cell6 = row.insertCell(5);
cell7 = row.insertCell(6);
cell8 = row.insertCell(7);
cell1.innerHTML = League[i][0];
cell2.innerHTML = League[i][1];
cell3.innerHTML = League[i][2];
cell4.innerHTML = League[i][3];
cell5.innerHTML = League[i][4];
cell6.innerHTML = League[i][5];
cell7.innerHTML = League[i][6];
cell8.innerHTML = League[i][7];
}
After entering three scores this is what is displayed:
I've tried clearing the league array within javascript but still the same outcome. How do I only display top version of the table? Thanks
Thanks again to comments, and some further googling, the following deletes the table ahead of updating it, unless there's a better way?
for(var i = standings.rows.length - 1; i >= 0; i--)
{
standings.deleteRow(i);
}
Cheers everyone! :)
For your table update/question, focus on the updateRow function. This line does the actual update of contents of row rownum column(<td>) i
rows[rownum].getElementsByTagName('td')[i].innerHTML = coldata[i];
There is more here than just updating the table rows, for that you can review the function updateRow in my name-spaced object. updateRow calls createRow if it needs to (the row at that index does not exist), nothing fancy here, then updates the new row.
I use the array of match objects in matches I created (was not one in the question so I made assumptions) also in the namespace:
matches: [{
match: 1,
score: [{
team: "Ap",
score: 3
}, {
team: "Or",
score: 2
}]
}],
Note where I call this code to update the table for standings in the table with standings-table id. I have no idea what those are so I simply inserted some stuff in the array then update the table using
for (let i = 0; i < myLeague.standings.length; i++) {
myLeague.updateRow('standings-table', myLeague.standings[i], i);
}
Other things: I created the form simply to show how to update the table when a new match is inserted, I trigger an event and it does what it needs to update or insert a row - but really that is just to test the update as new matches are created.
Row in a table are either updated or inserted depending totally on the array of matches content
nothing handles deletions from the table or array since this was just about insert and update
if a row index for a match index does not exist, it creates a new row and updates it
var myLeague = myLeague || {
teamSelect1: "team1",
teamSelect2: "team2",
matchesPlayed: 1,
teams: [{
name: "Apples",
abbreviation: "Ap"
},
{
name: "Oranges",
abbreviation: "Or"
},
{
name: "Pears",
abbreviation: "Pe"
}
],
matches: [{
match: 1,
score: [{
team: "Ap",
score: 3
}, {
team: "Or",
score: 2
}]
}],
standings: [
["A", 2, 1, 1, 3, 2, 3, 0],
["B", 3, 1, 1, 3, 2, 3, 6]
],
cloneRow: function(tableid, objectRef) {
// find table to clone/append to
let table = document.getElementById(tableid);
// find row to clone, I use first one
let firstRow = mytable.rows[0];
// let row = document.getElementById("rowToClone");
let clone = firstRow.cloneNode(true); // copy children too
clone.id = ""; // change id or other attributes/contents
table.appendChild(clone); // add new row to end of table
},
createRow: function(tableid, colCount, rowCount = 1, defaultContent = "") {
let row = document.createElement('tr'); // create row node
for (let i = 0; i < colCount; i++) {
let newText = document.createTextNode(defaultContent);
let col = row.insertCell(i);
col.appendChild(newText);
}
let table = document.getElementById(tableid); // find table to append to
let tbody = table.getElementsByTagName('tbody')[0];
for (let r = 1; r <= rowCount; r++) {
tbody.appendChild(row); // append row to table
}
},
updateRow: function(tableid, coldata = ['$nbsp;'], rownum = 0) {
let table = document.getElementById(tableid); // find table to update to
let tbody = table.getElementsByTagName('tbody')[0];
let rows = tbody.rows; // get rows node
let maxRows = 20; //keep it from going crazy adding rows
while (rows.length < maxRows && !rows[rownum]) {
this.createRow(tableid, coldata.length, 1, "x");
}
//myLeague.updateRow(tableid,coldata, rownum);
for (let i = 0; i < coldata.length; i++) {
rows[rownum].getElementsByTagName('td')[i].innerHTML = coldata[i];
}
},
addTeam: function(team, teamid) {
var sel = document.getElementById(teamid);
var optNew = document.createElement("option");
optNew.value = team.abbreviation;
optNew.text = team.name;
sel.add(optNew, null);
},
addTeamsToSelect: function() {
myLeague.teams.forEach(function(team) {
myLeague.addTeam(team, this.teamSelect1);
myLeague.addTeam(team, this.teamSelect2);
}, this);
},
listMatches: function(event) {
// event.target is the div
let src = event.target.dataset.source;
console.log("src:", src);
document.getElementById("matchplayed").textContent = event.matches;
this[src].forEach(function(item, index, array) {
document.getElementById('matchplayed').textContent = array.length;
let rowdata = [item.score[0].team, item.score[0].score, item.score[1].team, item.score[1].score];
this.updateRow(src, rowdata, index);
}, this);
},
clickAddListener: function(event) {
// 'this' is bound to the namespace object
// console.log(event.target); // the button
// console.log(this.matchesPlayed);//namespace
if (!document.getElementById(this.teamSelect1).value || !document.getElementById(this.teamSelect2).value) {
let errorEl = document.getElementById("form1")
.getElementsByClassName("error-text")[0];
errorEl.textContent = "Both teams need to be selected.";
errorEl.style.visibility = 'visible';
errorEl.style.opacity = '1';
setTimeout(function() {
errorEl.style.WebkitTransition = 'visibility .5s, opacity .5s';
errorEl.style.opacity = '0';
errorEl.style.visibility = 'hidden';
errorEl.textContent = "";
}, 5000);
} else {
this.matchesPlayed++;
let r = {
match: this.matchesPlayed,
score: [{
team: document.getElementById(this.teamSelect1).value,
score: document.getElementById("score1").value
}, {
team: document.getElementById(this.teamSelect2).value,
score: document.getElementById("score2").value
}]
};
this.matches.push(r);
}
document.getElementById('matches').dispatchEvent(this.showmatchesevent);
},
addListeners: function() {
let scope = this;
document.getElementById(this.teamSelect1)
.addEventListener('change', function() {
let s = document.getElementById(scope.teamSelect2);
let oval = s.value;
if (this.value == oval) {
s.value = '';
}
}, this);
document.getElementById(this.teamSelect2)
.addEventListener('change', function() {
let s = document.getElementById(scope.teamSelect1);
let oval = s.value;
if (this.value == oval) {
s.value = '';
}
}, this);
document.getElementById('add-match')
// bind this namespace to the event listener function
.addEventListener('click', (this.clickAddListener).bind(this), false);
this.showmatchesevent = new CustomEvent('showmatches');
document.getElementById('matches')
.addEventListener('showmatches', this.listMatches.bind(this), false);
}
};
window.onload = function() {
myLeague.addTeamsToSelect();
myLeague.addListeners();
for (let i = 0; i < myLeague.standings.length; i++) {
myLeague.updateRow('standings-table', myLeague.standings[i], i);
}
// set table from defaults/imported list
document.getElementById('matches').dispatchEvent(myLeague.showmatchesevent);
};
/* typography */
html {
font-family: 'helvetica neue', helvetica, arial, sans-serif;
}
th {
letter-spacing: 2px;
}
td {
letter-spacing: 1px;
}
tbody td {
text-align: center;
}
.match-inputs {
border: solid 2px #DDDDDD;
padding;
1em;
margin: 1em;
}
.error-text {
height: 1em;
color: red;
}
.matches-played {
padding: 13m;
}
/* table layout */
table {
border-collapse: collapse;
border: 1px solid black;
}
.score th,
td {
padding: 0.2em;
border: solid #DDDDDD 1px;
}
.container {
padding: 1em;
}
<div class="container match-inputs">
<form id="form1">
<div>Add Matches</div>
<div class="input-group"><label>Choose L Team:</label>
<select id="team1">
<option value="">Choose</option>
</select>
</div>
<div class="input-group"><label>Choose L2 Team:</label>
<select id="team2">
<option value="">Choose</option>
</select>
</div>
<div class="input-group score-group"><label>Team1 score:</label>
<input id="score1" type="number" class="score-input" value="0" min="0" max="99" value="0" />
</div>
<div class="input-group score-group"><label>Team2 score:</label>
<input id="score2" type="number" class="score-input" value="0" min="0" max="99" value="0" />
</div>
<div class="input-group"><label>Add this match to the list.</label>
<button type="button" id="add-match">Add Match</button>
</div>
<div class="error-text"> </div>
</form>
</div>
<div class="container">
<div class="matches-played">Matches Played:<span id="matchplayed"></span></div>
<table id="matches" data-source="matches">
<thead>
<tr>
<th colspan="4">Matches</th>
</tr>
<tr>
<th>L</th>
<th>S</th>
<th>S2</th>
<th>L1</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
</div>
<div class="container">
<table id="standings-table">
<thead>
<tr>
<th colspan="8">Standings</th>
</tr>
<tr>
<th>Team</th>
<th>P</th>
<th>W</th>
<th>D</th>
<th>L</th>
<th>F</th>
<th>A</th>
<th>Pts</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
</div>
I have a table with 5 columns. the following code will filter the data on basis of all columns. I want to filter the data for each column. for ex: if there are 10 columns then 10 search fields and Also how can I make HTML part Dynamic so that I don't have to add one more search text field whenever a new column is added.
<input id="myInput" type="text" />
<script>
function filterTable(event) {
var filter = event.target.value.toUpperCase();
var rows = document.querySelector("#myTable tbody").rows;
for (var i = 1; i < rows.length; i++) {
var Col1 = rows[i].cells[0].textContent.toUpperCase();
var Col2 = rows[i].cells[1].textContent.toUpperCase();
var Col3 = rows[i].cells[2].textContent.toUpperCase();
var Col4 = rows[i].cells[3].textContent.toUpperCase();
var Col5 = rows[i].cells[4].textContent.toUpperCase();
if (Col1.indexOf(filter) > -1 || Col2.indexOf(filter) > -1 || Col3.indexOf(filter) > -1
|| Col4.indexOf(filter) > -1 || Col5.indexOf(filter) > -1) {
rows[i].style.display = "";
} else {
rows[i].style.display = "none";
}
}
}
document.querySelector(''#myInput'').addEventListener(''keyup'', filterTable, false);
</script>
I want to have this kind of functionality:
You can loop over the rows and then the columns. The Array.from method allows you to cast an element list to an array so that you can iterate over the children with Array.prototype.forEach.
All you need to do is have a show initialized to true for each row. Then if any DOES NOT column meets the filter criteria, you set show to false. After looping through all columns you display the row based on the final value of show.
Edit: Make sure you are using a browser that supports ES6+. There is a polyfill available for Array.from on the MDN site.
function filterTable(event) {
let filter = event.target.value.trim().toLowerCase();
let rows = document.querySelector('#myTable tbody').rows;
for (let i = 0; i < rows.length; i++) {
let row = rows[i], show = false;
if (filter.length > 0) {
for (let j = 0; j < row.children.length; j++) {
let col = row.children[j], text = col.textContent.toLowerCase();
if (text.indexOf(filter) > -1) {
show = true;
continue;
}
}
} else {
show = true;
}
// Avoid using 'row.styledisplay' - https://stackoverflow.com/a/28028656/1762224
// Avoid using 'row.visibility' - rows do not collapse
toggleClass(row, 'hidden-row', !show);
}
}
function toggleClass(el, className, state) {
if (el.classList) el.classList.toggle(className, state);
else {
var classes = el.className.split(' ');
var existingIndex = classes.indexOf(className);
if (state === undefined) {
if (existingIndex > -1) classes.splice(existingIndex, 1)
else classes.push(existingIndex);
} else {
if (!state) classes.splice(existingIndex, 1)
else classes.push(existingIndex);
}
el.className = classes.join(' ');
}
}
document.querySelector('#myInput').addEventListener('keyup', filterTable, false);
body {
padding: 8px;
}
.field label {
font-weight: bold;
margin-right: 0.25em;
}
#myTable {
margin-top: 0.667em;
width: 100%;
}
#myTable th {
text-transform: capitalize;
}
.hidden-row {
display: none;
}
<link href="https://unpkg.com/purecss#1.0.0/build/pure-min.css" rel="stylesheet" />
<div class="field"><label for="myInput">Filter:</label><input id="myInput" type="text" /></div>
<table id="myTable" class="pure-table pure-table-horizontal">
<thead>
<tr>
<th>name</th>
<th>drink</th>
<th>pizza</th>
<th>movie</th>
</tr>
</thead>
<tbody>
<tr>
<td>Homer</td>
<td>Squishie</td>
<td>Magheritta</td>
<td>The Avengers</td>
</tr>
<tr>
<td>Marge</td>
<td>Squishie</td>
<td>Magheritta</td>
<td>The Avengers</td>
</tr>
<tr>
<td>Bart</td>
<td>Squishie</td>
<td>Pepperoni</td>
<td>Black Dynamite</td>
</tr>
<tr>
<td>Lisa</td>
<td>Buzz Cola</td>
<td>Pepperoni</td>
<td>Iron Man</td>
</tr>
<tr>
<td>Maggie</td>
<td>Duff Beer</td>
<td>Magheritta</td>
<td>The Avengers</td>
</tr>
<tr>
<td>Kent</td>
<td>Duff Beer</td>
<td>Hawaiian</td>
<td>The Avengers</td>
</tr>
</tbody>
</table>
Search fields example
populateTable(document.getElementById('simpsons'), getData());
function dataFields(data) {
return data.reduce((r, x) => Object.keys(x).reduce((s, k) => s.indexOf(k) === -1 ? s.concat(k) : s, r), []);
}
/* Can be useful if working with raw JSON data */
function searchCriteria(fields) {
return Array.from(fields).reduce((o, field) => {
return Object.assign(o, { [field.getAttribute('placeholder')] : field.value });
}, {});
}
function onFilter(e) {
let table = e.target.parentElement.parentElement.parentElement.parentElement;
let fields = table.querySelectorAll('thead tr th input');
let criteria = searchCriteria(fields); // Unused, but useful if filtering bindable data
let searchText = Array.from(fields).map(field => field.value.trim());
Array.from(table.querySelectorAll('tbody tr')).forEach(row => {
let hideRow = false;
Array.from(row.children).forEach((col, index) => {
var value = col.innerHTML.trim().toLowerCase();
var search = searchText[index].toLowerCase();
if (search.length > 0) {
if (!value.startsWith(search)) { /* or value.indexOf(search) === -1 */
hideRow = true;
return;
}
}
});
row.classList.toggle('hidden-row', hideRow);
});
}
function populateTable(table, data) {
let fields = dataFields(data);
let thead = document.createElement('THEAD');
let tr = document.createElement('TR');
fields.forEach(field => {
let th = document.createElement('TH');
th.innerHTML = field;
tr.appendChild(th);
});
thead.appendChild(tr);
let tbody = document.createElement('TBODY');
tr = document.createElement('TR');
fields.forEach(field => {
let th = document.createElement('TH');
let input = document.createElement('INPUT');
input.setAttribute('placeholder', field);
input.addEventListener('keyup', onFilter);
th.append(input);
tr.appendChild(th);
});
thead.appendChild(tr);
data.forEach(record => {
let tr = document.createElement('TR');
fields.forEach(field => {
let td = document.createElement('TD');
td.innerHTML = record[field];
tr.appendChild(td);
});
tbody.append(tr);
});
table.appendChild(thead);
table.appendChild(tbody);
}
function getData() {
return [{
"name": "Homer",
"drink": "Squishie",
"pizza": "Magheritta",
"movie": "The Avengers"
}, {
"name": "Marge",
"drink": "Squishie",
"pizza": "Magheritta",
"movie": "The Avengers"
}, {
"name": "Bart",
"drink": "Squishie",
"pizza": "Pepperoni",
"movie": "Black Dynamite"
}, {
"name": "Lisa",
"drink": "Buzz Cola",
"pizza": "Pepperoni",
"movie": "Iron Man"
}, {
"name": "Maggie",
"drink": "Duff Beer",
"pizza": "Magheritta",
"movie": "The Avengers"
}, {
"name": "Kent",
"drink": "Duff Beer",
"pizza": "Hawaiian",
"movie": "The Avengers"
}];
}
table {
border-collapse: collapse;
width: 100%;
}
table thead tr th {
text-transform: capitalize;
}
table thead tr:last-child {
background: #eaeaea;
border-bottom: 4px double #cbcbcb;
}
table thead tr th input {
width: 100%;
}
table tbody tr:nth-child(even) {
background-color: #f2f2f2;
}
table tbody tr.hidden-row {
display: none;
}
<link href="https://unpkg.com/purecss#1.0.0/build/pure-min.css" rel="stylesheet" />
<table id="simpsons" class="pure-table pure-table-horizontal"></table>
Browser Compatibility
function onFilter(e) {
var table = e.target.parentElement.parentElement.parentElement.parentElement;
console.log(table);
var fields = table.querySelectorAll('thead tr th input');
console.log(fields);
var searchText = Array.from(fields).map(function (field) {
return field.value.trim();
});
console.log(searchText);
Array.from(table.querySelectorAll('tbody tr')).forEach(function (row) {
var hideRow = false;
Array.from(row.children).forEach(function (col, index) {
var value = col.innerHTML.trim().toLowerCase();
console.log(value);
var search = searchText[index].toLowerCase();
console.log(search);
if (search.length > 0) {
if (value.indexOf(search) === -1) {
hideRow = true;
return;
}
}
});
row.classList.toggle('hidden-row', hideRow);
});
}
All you have to do is iterate the .cells array with a for loop:
For this example, I used a variable to determine if the row should be shown.
function filterTable(event) {
var filter = event.target.value.toUpperCase();
var rows = document.querySelector("#myTable tbody").rows;
for (var i = 1; i < rows.length; i++) {
// Placeholder to indicate if a row matches the given query.
var shouldShowRow = false;
// Loop over all the cells in this row.
for (var k = 0; k < rows[i].cells.length) {
// Check to see if this cell in this row matches the query.
if (rows[i].cells[k].textContent.toUpperCase().indexOf(filter) > -1) {
// Yes, this cell matches, therefore this entire row matches.
// Flip the show row boolean to indicate that we need to show.
shouldShowRow = true;
// Because this cell matches, we do not need to check any more cells.
break;
}
}
// Change the display on the row if we need to.
if (shouldShowRow) {
rows[i].style.display = "";
} else rows[i].style.display = "none";
}
}
You can use array.
First map col to an array colArr and than using some you can match you can match filter content with content of cols .
for (var i = 1; i < rows.length; i++) {
let colArr = new Array(5).fill(0).map((e,index)=> rows[i].cells[index].textContent.toUpperCase();)
if (colArr.some(e=> e.indexOf(filter) > -1 ) {
rows[i].style.display = "";
} else {
rows[i].style.display = "none";
}
})
}
I'm trying to convert table string (like csv) to html table.
My code works fine with simple table but when it merged row and column it fails. so how do i can use rowspan and column span in the script?
<!DOCTYPE html>
<html ng-app="" ng-controller="myCtrl">
<style>
table, th, td {
border: 1px solid black;
padding:5px;
}
table {
border-collapse: collapse;
margin:10px;
}
</style>
<body>
<button ng-click="processData(allText)">
Display CSV as Data Table
</button>
<div id="divID">
<table>
<tr ng-repeat="x in data">
<td ng-repeat="y in x">{{ y }}</td>
</tr>
</table>
</div>
<div>
<table>
</table>
</div>
JS
<script>
function myCtrl($scope, $http) {
$scope.allText="Series |Wire Range\n (AWG) |"+
"Wire Type |FW |Voltage\n (V) |Current \n (A) |"+
"UG |CA |\nSL-6100 RS#2|26 16, SOL,\n Unprepared |"+
"Cu RS#2|2 RS#2|300 RS#2|7 RS#2|B, D RS#2|"+
"2(105), 4 RS#2|\n24 - 16, STR,\n Unprepared |"+
"\nNominal Strip length: 9 - 10 mm CS#8|"+
"\nEnvironmental - Maximum ambient temperature"+
" rating for CNR: 85 C CS#8|\n";
$scope.processData = function(allText) {
// split content based on new line
var allTextLines = allText.split(/\|\n|\r\n/);
var headers = allTextLines[0].split('|');
var lines = [];
for ( var i = 0; i < allTextLines.length; i++) {
// split content based on comma
var data = allTextLines[i].split('|');
if (data.length == headers.length) {
var temp = [];
for ( var j = 0; j < headers.length; j++) {
temp.push(data[j]);
}
lines.push(temp);
}
};
$scope.data = lines;
};
}
</script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular.min.js"></script>
</body>
</html>
RS#2 ---indicates rowspan of 2
cs#8 ---indicates colspan of 8
| ---is the dilimiter for cell
|\n ---for new line
and value in $scope.allText is CSV table string
so essentially i should get this table as output-
You can create an object with rows and cols so that you can use that as rowspan and colspan.
Like this
Demo
$scope.processData = function(allText) {
// split content based on new line
var allTextLines = allText.split(/\|\n|\r\n/);
var headers = allTextLines[0].split('|');
var lines = [];
var r,c;
for ( var i = 0; i < allTextLines.length; i++) {
// split content based on comma
var data = allTextLines[i].split('|');
if (data.length == headers.length) {
var temp = [];
for ( var j = 0; j < headers.length; j++) {
if(data[j].indexOf("RS") !== -1) {
r = data[j].split("#").reverse()[0];
}
else {
r = 0;
}
if(data[j].indexOf("CS") !== -1) {
c = data[j].split("#").reverse()[0];
}
else {
c = 0;
}
temp.push({"rows":r,"cols":c,"data":data[j]});
}
lines.push(temp);
}
}
alert(JSON.stringify(lines));
$scope.data = lines;
}
You can add CS to your string and alter conditions as required in this code.
Controller
function myCtrl($scope, $http) {
$scope.allText = "Series |Wire Range\n (AWG) |Wire Type |FW |Voltage\n (V) |Current \n (A) |UG |CA |\nSL-6100 RS#2|26 16, SOL,\n Unprepared |Cu RS#2|2 RS#2|300 RS#2|7 RS#2|B, D RS#2|2(105), 4 RS#2|\n24 - 16, STR,\n Unprepared |\nNominal Strip length: 9 - 10 mm CS#8|\nEnvironmental - Maximum ambient temperature rating for CNR: 85 C CS#8";
$scope.processData = function (allText) {
var table = [];
allText.split(/\|\n|\r\n/).forEach(function (line) {
var tr = [];
line.split('|').forEach(function (cell) {
tr.push({
text: cell.replace(/RS#.*$/, '').replace(/CS#.*$/, ''),
rowspan: (cell + 'RS#1').replace(/^[\S\s]*?RS#(\d*).*$/, '$1'),
colspan: (cell + 'CS#1').replace(/^[\S\s]*?CS#(\d*).*$/, '$1'),
})
})
table.push(tr)
});
$scope.table = table;
};
}
HTML
<table>
<tr ng-repeat="tr in table">
<td ng-repeat="td in tr" ng-attr-colspan="{{td.colspan}}" ng-attr-rowspan="{{td.rowspan}}">{{ td.text }}</td>
</tr>
</table>
Code Snippet
function myCtrl($scope, $http) {
$scope.allText = "Series |Wire Range\n (AWG) |Wire Type |FW |Voltage\n (V) |Current \n (A) |UG |CA |\nSL-6100 RS#2|26 16, SOL,\n Unprepared |Cu RS#2|2 RS#2|300 RS#2|7 RS#2|B, D RS#2|2(105), 4 RS#2|\n24 - 16, STR,\n Unprepared |\nNominal Strip length: 9 - 10 mm CS#8|\nEnvironmental - Maximum ambient temperature rating for CNR: 85 C CS#8";
$scope.processData = function (allText) {
var table = [];
allText.split(/\|\n|\r\n/).forEach(function (line) {
var tr = [];
line.split('|').forEach(function (cell) {
tr.push({
text: cell.replace(/RS#.*$/, '').replace(/CS#.*$/, ''),
rowspan: (cell + 'RS#1').replace(/^[\S\s]*?RS#(\d*).*$/, '$1'),
colspan: (cell + 'CS#1').replace(/^[\S\s]*?CS#(\d*).*$/, '$1'),
})
})
table.push(tr)
});
$scope.table = table;
};
}
angular.module('myApp', [])
.controller('ctrlr', myCtrl)
table, th, td {
border: 1px solid black;
padding: 5px;
}
table {
border-collapse: collapse;
margin: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="ctrlr">
<button ng-click="processData(allText)">
Display CSV as Data Table
</button>
<div id="divID">
<table>
<tr ng-repeat="tr in table">
<td ng-repeat="td in tr" ng-attr-colspan="{{td.colspan}}" ng-attr-rowspan="{{td.rowspan}}">{{ td.text }}</td>
</tr>
</table>
</div>
<div>
<table></table>
</div>
</div>
</div>