Javascript set value of specific table cell - javascript

I am having an issue with populating an HTML table with the correct data dynamically via javascript and jQuery.
The table shows some numbers based on gender, and every time the user makes a selection, they get back new data from the server, which populates the table. There are two rows and 4 cells. I dynamically insert the cells and update the text via javascript every time the API returns new data. The problem is that the data is being populated in the wrong table cells -- the number for "female / cell 2" ends up in the "female / cell 3" and so forth. I can't figure out how to target the correct cells based on the data. Here is a minimal reproduction via codepen, or see below for code as well:
Also I did look at the following threads but didn't see anything that seemed like it addressed my issue:
s/o thread 1 -----
s/o thread 2
The data is very simple and looks like this -- there are positive, neutral, and negative percentages for female and male:
let genderData = [
{ gender: "female", percent: 36, score: "neutral" },
{ gender: "female", percent: 1, score: "negative" },
{ gender: "female", percent: 18, score: "positive" },
{ gender: "male", percent: 27, score: "neutral" },
{ gender: "male", percent: 14, score: "positive" },
{ gender: "male", percent: 1, score: "negative" }
];
The HTML table looks like this, with the header row hard-coded in HTML:
<!-- Gender Table -->
<table id="sentiment-table__gender" class="table">
<thead class="sticky">
<tr id="sentiment-gender-tr">
<!-- gender -->
<th class="sticky left-align table-th">Gender</th>
<!-- percent positive posts -->
<th class="sticky right-align table-th">Positive Posts</th>
<!-- percent neutral posts -->
<th class="sticky right-align table-th">Neutral Posts</th>
<!-- percent Negative posts -->
<th class="sticky right-align table-th">Negative Posts</th>
</tr>
</thead>
<tbody id="sentiment-page-table__body"></tbody>
</table>
What I need to do is populate the table with the percentages for each sentiment, categorized by gender. The "female" item with a "neutral" score of "36%" should appear in the female row / cell 2 (neutral), but instead appears in a different cell. I suspect this is because the data is not in a specific order, so I can't just populate the cells one by one. I can't change anything about the backend or how the data is returned.
I loop through the data, and then write conditionals to separate the female and male data. Then I write a switch statement based on the sentiment and insert cells for each item. I know what I'm missing is some way to specify which cell the data point should be inserted into. I tried specifying it by saying newFemaleRow.insertCell(2) (or whichever cell I wanted to target), but that threw an error:
sentiment.js:280 Uncaught DOMException:
Failed to execute 'insertCell' on 'HTMLTableRowElement':
The value provided (2) is outside the range [-1, 1].
See below for a full snippet including my JS, and here's that codepen link one more time too.
$(function () {
populateTable();
});
let genderData = [
{ gender: "female", percent: 36, score: "neutral" },
{ gender: "female", percent: 1, score: "negative" },
{ gender: "female", percent: 18, score: "positive" },
{ gender: "male", percent: 27, score: "neutral" },
{ gender: "male", percent: 14, score: "positive" },
{ gender: "male", percent: 1, score: "negative" }
];
const populateTable = () => {
// Local variables
let genderTable = $("#sentiment-table__gender");
// Store table id in variable
let tableRef = document.getElementById("sentiment-table__gender");
// Clear out table data before loading new results
$(tableRef).find("tr:gt(0)").remove();
let newMaleRow = tableRef.insertRow(-1);
let newFemaleRow = tableRef.insertRow(-1);
let maleGenderCell = newMaleRow.insertCell(0);
let maleGenderText = document.createTextNode("Male");
maleGenderCell.className = "left-align";
maleGenderCell.appendChild(maleGenderText);
let femaleGenderCell = newFemaleRow.insertCell(0);
let femaleGenderText = document.createTextNode("Female");
femaleGenderCell.className = "left-align";
femaleGenderCell.appendChild(femaleGenderText);
// Loop through the data to populate the table
for (let item of genderData) {
// MALE
if (item.gender === "male") {
switch (item.score) {
case "negative":
let negativeMalePostsCell = newMaleRow.insertCell();
let negativeMalePostsText = document.createTextNode(
item.percent.toLocaleString("en-US") + "%"
);
negativeMalePostsCell.className = "right-align";
negativeMalePostsCell.appendChild(negativeMalePostsText);
break;
case "neutral":
let neutralMalePostsCell = newMaleRow.insertCell();
let neutralMalePostsText = document.createTextNode(
item.percent.toLocaleString("en-US") + "%"
);
neutralMalePostsCell.className = "right-align";
neutralMalePostsCell.appendChild(neutralMalePostsText);
break;
case "positive":
let positiveMalePostsCell = newMaleRow.insertCell();
let positiveMalePostsText = document.createTextNode(
item.percent.toLocaleString("en-US") + "%"
);
positiveMalePostsCell.className = "right-align";
positiveMalePostsCell.appendChild(positiveMalePostsText);
break;
}
}
// FEMALE
if (item.gender === "female") {
switch (item.score) {
case "negative":
let negativeFemalePostsCell = newFemaleRow.insertCell();
let negativeFemalePostsText = document.createTextNode(
item.percent.toLocaleString("en-US") + "%"
);
negativeFemalePostsCell.className = "right-align";
negativeFemalePostsCell.appendChild(negativeFemalePostsText);
break;
case "neutral":
let neutralFemalePostsCell = newFemaleRow.insertCell();
let neutralFemalePostsText = document.createTextNode(
item.percent.toLocaleString("en-US") + "%"
);
neutralFemalePostsCell.className = "right-align";
neutralFemalePostsCell.appendChild(neutralFemalePostsText);
break;
case "positive":
let positiveFemalePostsCell = newFemaleRow.insertCell();
let positiveFemalePostsText = document.createTextNode(
item.percent.toLocaleString("en-US") + "%"
);
positiveFemalePostsCell.className = "right-align";
positiveFemalePostsCell.appendChild(positiveFemalePostsText);
break;
}
}
}
};
/* ==================================================
Table styling
================================================== */
.table {
text-overflow: wrap;
border-collapse: collapse;
border-spacing: 0;
width: 80%;
border: 1px solid #ddd;
position: relative;
}
/* GENERAL TABLE STYLING */
.sticky {
position: sticky;
position: -webkit-sticky;
top: -1px;
background: white;
}
.table-th,
.table td {
padding: 8px;
height: 40px;
}
.right-align {
text-align: right;
}
.center-align {
text-align: center;
}
.left-align {
text-align: left;
}
.table tr:nth-child(even) {
background-color: orange;
}
.border {
border-bottom: 1px inset black;
}
<!-- jQuery -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<!-- jQuery UI -->
<script src="https://code.jquery.com/ui/1.13.1/jquery-ui.js"></script>
<!-- Gender Table -->
<table id="sentiment-table__gender" class="table">
<colgroup>
<col span="1" />
<col span="1" />
<col span="1" />
<col span="1" />
</colgroup>
<thead class="sticky">
<tr id="sentiment-gender-tr">
<!-- gender -->
<th class="sticky left-align table-th">Gender</th>
<!-- percent positive posts -->
<th class="sticky right-align table-th">Positive Posts</th>
<!-- percent neutral posts -->
<th class="sticky right-align table-th">Neutral Posts</th>
<!-- percent Negative posts -->
<th class="sticky right-align table-th">Negative Posts</th>
</tr>
</thead>
<tbody id="sentiment-page-table__body"></tbody>
</table>

Shoot, this should have been easy. I simply need to insert the JS dynamically in the same order as the HTML! All I did was switch around the order of the cases in the switch statement to match the order of the cells in the table header, and boom, all the data populated in the correct cell.
Here is an updated codepen
$(function () {
populateTable();
});
let genderData = [
{ gender: "female", percent: 36, score: "neutral" },
{ gender: "female", percent: 1, score: "negative" },
{ gender: "female", percent: 18, score: "positive" },
{ gender: "male", percent: 27, score: "neutral" },
{ gender: "male", percent: 14, score: "positive" },
{ gender: "male", percent: 1, score: "negative" }
];
const populateTable = () => {
// Local variables
let genderTable = $("#sentiment-table__gender");
// Store table id in variable
let tableRef = document.getElementById("sentiment-table__gender");
// Clear out table data before loading new results
$(tableRef).find("tr:gt(0)").remove();
let newMaleRow = tableRef.insertRow(-1);
let newFemaleRow = tableRef.insertRow(-1);
let maleGenderCell = newMaleRow.insertCell(0);
let maleGenderText = document.createTextNode("Male");
maleGenderCell.className = "left-align";
maleGenderCell.appendChild(maleGenderText);
let femaleGenderCell = newFemaleRow.insertCell(0);
let femaleGenderText = document.createTextNode("Female");
femaleGenderCell.className = "left-align";
femaleGenderCell.appendChild(femaleGenderText);
// Loop through the data to populate the table
for (let item of genderData) {
// MALE
if (item.gender === "male") {
switch (item.score) {
case "neutral":
let neutralMalePostsCell = newMaleRow.insertCell();
let neutralMalePostsText = document.createTextNode(
item.percent.toLocaleString("en-US") + "%"
);
neutralMalePostsCell.className = "right-align";
neutralMalePostsCell.appendChild(neutralMalePostsText);
break;
case "negative":
let negativeMalePostsCell = newMaleRow.insertCell();
let negativeMalePostsText = document.createTextNode(
item.percent.toLocaleString("en-US") + "%"
);
negativeMalePostsCell.className = "right-align";
negativeMalePostsCell.appendChild(negativeMalePostsText);
break;
case "positive":
let positiveMalePostsCell = newMaleRow.insertCell();
let positiveMalePostsText = document.createTextNode(
item.percent.toLocaleString("en-US") + "%"
);
positiveMalePostsCell.className = "right-align";
positiveMalePostsCell.appendChild(positiveMalePostsText);
break;
}
}
// FEMALE
if (item.gender === "female") {
switch (item.score) {
case "neutral":
let neutralFemalePostsCell = newFemaleRow.insertCell();
let neutralFemalePostsText = document.createTextNode(
item.percent.toLocaleString("en-US") + "%"
);
neutralFemalePostsCell.className = "right-align";
neutralFemalePostsCell.appendChild(neutralFemalePostsText);
break;
case "negative":
let negativeFemalePostsCell = newFemaleRow.insertCell();
let negativeFemalePostsText = document.createTextNode(
item.percent.toLocaleString("en-US") + "%"
);
negativeFemalePostsCell.className = "right-align";
negativeFemalePostsCell.appendChild(negativeFemalePostsText);
break;
case "positive":
let positiveFemalePostsCell = newFemaleRow.insertCell();
let positiveFemalePostsText = document.createTextNode(
item.percent.toLocaleString("en-US") + "%"
);
positiveFemalePostsCell.className = "right-align";
positiveFemalePostsCell.appendChild(positiveFemalePostsText);
break;
}
}
}
};
/* ==================================================
Table styling
================================================== */
.table {
text-overflow: wrap;
border-collapse: collapse;
border-spacing: 0;
width: 80%;
border: 1px solid #ddd;
position: relative;
}
/* GENERAL TABLE STYLING */
.sticky {
position: sticky;
position: -webkit-sticky;
top: -1px;
background: white;
}
.table-th,
.table td {
padding: 8px;
height: 40px;
}
.right-align {
text-align: right;
}
.center-align {
text-align: center;
}
.left-align {
text-align: left;
}
.table tr:nth-child(even) {
background-color: orange;
}
.border {
border-bottom: 1px inset black;
}
<!-- jQuery -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<!-- jQuery UI -->
<script src="https://code.jquery.com/ui/1.13.1/jquery-ui.js"></script>
<!-- Gender Table -->
<table id="sentiment-table__gender" class="table">
<colgroup>
<col span="1" />
<col span="1" />
<col span="1" />
<col span="1" />
</colgroup>
<thead class="sticky">
<tr id="sentiment-gender-tr">
<!-- gender -->
<th class="sticky left-align table-th">Gender</th>
<!-- percent neutral posts -->
<th class="sticky right-align table-th" id='neutral-posts-th'>Neutral Posts</th>
<!-- percent Negative posts -->
<th class="sticky right-align table-th" id='negative-posts-th'>Negative Posts</th>
<!-- percent positive posts -->
<th class="sticky right-align table-th" id='positive-posts-th'>Positive Posts</th>
</tr>
</thead>
</table>

Related

How to create an HTML table from an array of objects?

I need to generate a table from an array of objects.
For example, the array is:
let arr = [{name: 'Player1',score:10},
{name: 'Player2',score: 7},
{name: 'Player3',score:3}]
And the HTML output should be:
Name
Score
Player1
10
PLayer2
7
Player3
3
I could not think of a way to achieve this through vanilla JS.
Also, after the table is created, how will I apply CSS to it?
Any help would be appreciated.
You can loop through the array and for each element add a row to a newly forged table that will be added to the document at the end.
This is a demo:
let players = [
{name: 'Player1',score:10},
{name: 'Player2',score: 7},
{name: 'Player3',score:3}
];
const newTable = document.createElement("table");
newTable.innerHTML = "<thead><th>Player</th><th>Score</th></thead>";
for(player of players){
const newRow = document.createElement("tr");
const tdPlayer = document.createElement("td");
const tdScore = document.createElement("td");
tdPlayer.textContent = player.name;
tdScore.textContent = player.score;
newRow.appendChild(tdPlayer);
newRow.appendChild(tdScore);
newTable.appendChild(newRow);
}
const target = document.getElementById('target');
target.appendChild(newTable);
table{
border: solid 1px black;
}
table td{
border: solid 1px black;
}
<div id="target">
</div>
You can use something like
<body>
<div class="main-container">
<table>
<thead>
<tr>
<th>player</th>
<th>score</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<script>
const data = [{ name: 'Player 1', score: 10 },
{ name: 'Player 2', score: 7 },
{ name: 'Player 3', score: 3 }]
const table = document.querySelector('tbody')
data.forEach((item) => {
table.innerHTML = table.innerHTML + `<tr>
<td>${item.name}</td>
<td>${item.score}</td>
</tr>`
})
</script>

How do I display a dynamically created html table only once?

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>

HTML Table Not Updating With CSS

I am attempting to make a table that has the equivalent of the Bootstrap table-hover feature along with the table-striped feature. However, the CSS does not seem to be applying properly. The th styling is applying however none of the rows are. Additionally, I know the toAppend lines should probably be more clear but that is not my current concern.
This is what the table currently looks like:
JavaScript:
function fortniteSearch(data) {
// data to retrieve data / construct table
const tableCells = [
{category: "lifeTimeStats", stats: [8, 9, 10, 11, 7, 2, 4, 5]},
{category: "stats", subcategory: "p2", stats: ["top1", "winRatio", "kills", "kd", "kpg", "matches", "top10", "top25"]},
{category: "stats", subcategory: "p10", stats: ["top1", "winRatio", "kills", "kd", "kpg", "matches", "top5", "top12"]},
{category: "stats", subcategory: "p9", stats: ["top1", "winRatio", "kills", "kd", "kpg", "matches", "top3", "top6"]}];
// cell labels and flag
const gameModes = ["OVERALL:", "SOLO:", "DUO:", "SQUADS:"];
// check for errors
if(data.error !== undefined)
alert("Player not found!");
else {
// set player name and image
$("#playerName").val(data.IGN);
$("#playerIcon").attr("src", "/images/avatar_fortnite.png");
// show stats
$("#statDisplay").fadeIn(500);
// clear pre-existing table
$("#statTable").empty();
// iterate through the table
for(let i = 0; i < tableCells.length; i++) {
// set header row
$("#statTable").append('<thead> <tr> <th align = "center" colspan = 8> <b> <u>' + gameModes[i] + "</u> </b> </th> </tr> </thead> <tbody> <tr>");
// intialize stats
let toAppend;
// iterate through stats
for(let j = 0; j < tableCells[i].stats.length; j++) {
// if there is no sub-category (use different html)
if(tableCells[i].subcategory === undefined)
toAppend = ("<td> <b>" + data[tableCells[i].category][tableCells[i].stats[j]].key + ":</b>" + "<br>" + data[tableCells[i].category][tableCells[i].stats[j]].value + "</td>");
else
toAppend = ("<td> <b>" + data[tableCells[i].category][tableCells[i].subcategory][tableCells[i].stats[j]].label + ":</b>" + "<br>" + data[tableCells[i].category][tableCells[i].subcategory][tableCells[i].stats[j]].value + "</td>");
// add html
$(".statTable").append(toAppend);
}
// close table row
$(".statTable").append("</tr> </tbody>");
}
// hide game window
$("#gameSelect").css("display", "none");
}
}
CSS:
#statTable {
width: 95%;
height: 10%;
margin-left: auto;
margin-right: auto;
margin-top: 10px;
text-align: center;
border: 1px solid black;
background-color: white;
}
#statTable th {
background-color: green;
color: white;
text-align: center;
}
#statTable tr:nth-child(even) {
background: grey;
}
#statTable tr:hover td {
background-color: red;
}
HTML:
<body>
<div class = "standardImage" id = "statDisplay">
<div name = "statBanner" id = "statBanner" class = "statBanner">
<img src = "/images/icon_xbox.png" name = "playerIcon" class = "playerIcon" id = "playerIcon" alt = "Player Icon">
<input name = "playerName" class = "playerName" id = "playerName" readonly>
<span name = "statButtons" id = "statButtons" class = "statButtons">
<button name = "backButton" class = "backButton" id = "backButton"> BACK </button>
<button name = "refreshButton" class = "refreshButton" id = "refreshButton"> REFRESH </button>
</span>
</div>
<table class = "statTable" id = "statTable"> <thead> </thead> <tbody> </tbody> </table>
</div>
</body>

Create a generic sortableTable object to be used to alphabetically sort a table elements each time a column is clicked using only JavaScript

I'm working on project which don't use jQuery but only JavaScript and we have a need to sort alphabetically ascending a table values (without any external library).
The objective is to sort rows elements each time a column (th element) is clicked.
Example
If I have this table
When I click on "Names", the table should be refreshed like that:
When I click on "Tel", the table should be refreshed like that:
I'm sharing my own solution here. If you have any remark or suggestions, please share.
I'm updating my solution by adding an inverse order feature:
When you first click on Names for example, the table will be refreshed with an ascending order of names.
When you click a 2nd time on Names, the table will be refreshed with a descending order of names.
When you click a 3rd time on Names, the table will be refreshed with an ascending order of names.
etc...
var sortableTable = {
/**
* The table to sort
*/
table: null,
getTable: function(){
return this.table;
},
setTable: function(table){
this.table = table;
},
/**
* The column used for sorting
*/
element: null,
getElement: function(){
return this.element;
},
setElement: function(element){
this.element = element;
},
/**
* When ooderDirection is 1 => Ascending order
* When ooderDirection is -1 => Descending order
*/
orderDirection: 1,
getOrderDirection: function(){
return this.orderDirection;
},
setOrderDirection: function(orderDirection){
this.orderDirection = orderDirection;
},
/**
* Get table rows elements
*/
getRows: function(){
var rows = [];
if(null !== this.getTable()){
var allRows = this.getTable().rows;
/*
When I returned allRows directly,
in the sort function when I do: rows.sort();
it display an error: Uncaught TypeError: rows.sort is not a function
So I'm converting this object to an array
*/
var arrayRows = [];
//allRows contains all rows with <th> elements,
//so I'm removing th elements (index 0)
for(let i=1 ; i<allRows.length ; i++){
arrayRows.push(allRows[i]);
}
return (arrayRows);
}
return null;
},
/**
* Display rows using the sort result
*/
refresh: function(rows){
for(let i=0 ; i<rows.length ; i++){
this.getTable().appendChild(rows[i]);
}
},
/**
* Sort alphabetically (ASC)
*/
sort: function(indexOfClickedTh){
var rows = this.getRows();
var that = this;
rows.sort(function(item1, item2){
var contentItem1 = item1.getElementsByTagName('td')[indexOfClickedTh].innerText;
var contentItem2 = item2.getElementsByTagName('td')[indexOfClickedTh].innerText;
let resultCompare = contentItem1.localeCompare(contentItem2);
resultCompare = resultCompare * that.getOrderDirection();
//console.info('comparing(' + contentItem1 + ', ' + contentItem2 + ')=' + resultCompare);
return resultCompare;
});
this.refresh(rows);
}
}
//The first click will generate an ascending sort
var initialOderDirection = -1;
var myTableToSort = document.getElementById('users');
sortableTable.setTable(myTableToSort);
sortableTable.setOrderDirection(initialOderDirection);
var ListOfTh = document.getElementById('users').getElementsByTagName('th');
for(var i=0 ; i<ListOfTh.length ; i++){
var oneTh = ListOfTh[i];
oneTh.addEventListener("click", function(){
//console.info("------> New sort based on '" + this.innerText + "' <------");
// Set the current clicked <th> element
sortableTable.setElement(this);
//Inverse the order
sortableTable.setOrderDirection( sortableTable.getOrderDirection() * -1 );
//Do the sort and refresh table result
sortableTable.sort(this.cellIndex);
});
};
table{
font-size: 16px;
border-collapse: collapse;
border-spacing: 0;
width: 100%;
}
table th{
padding-top: 11px;
padding-bottom: 11px;
background-color: #6295a5;
color: white;
}
table td{
border: 1px solid #ddd;
text-align: left;
padding: 8px;
}
table tr:nth-child(even) {
background-color: #f2f2f2;
}
table th{
cursor: pointer;
}
table th:hover{
color: #dea82e;
background-color: #37545d;
}
<table id="users">
<thead>
<tr>
<th>Names</th>
<th>Functions</th>
<th>Emails</th>
<th>Tel</th>
</tr>
</thead>
<tbody>
<tr>
<td>xMxxx</td>
<td>Physicists</td>
<td>xmxxx#domain.com</td>
<td>00 55 99 99 99</td>
</tr>
<tr>
<td>xJxxx</td>
<td>Air Traffic Controllers</td>
<td>xjxxx#domain.com</td>
<td>00 22 99 99 99</td>
</tr>
<tr>
<td>xExxx</td>
<td>Engineer</td>
<td>xexxx#domain.com</td>
<td>00 33 99 99 99</td>
</tr>
<tr>
<td>xAxxx</td>
<td>Mechanical engineer</td>
<td>xaxxx#domain.com</td>
<td>00 11 99 99 99</td>
</tr>
<tr>
<td>xZxxx</td>
<td>Doctor</td>
<td>xzxxx#domain.com</td>
<td>00 44 99 99 99</td>
</tr>
<tr>
<td>xPxxx</td>
<td>Professor</td>
<td>xpxxx#domain.com</td>
<td>00 66 99 99 99</td>
</tr>
</tbody>
</table>

Javascript / Jquery Help - Calling functions from select menu

I Need some help with adding interactivity to this page.
I'm new to jQuery and this stuff is probably simple but its been driving me nuts!
Its just a footy team with different players details stored in objects in an array called Squad_list
Squad_List.js
var squad = [
{
number: 1,
pic: 'img/HIBBERD_M_t.png',
name: 'Michael',
surname: 'Hibberd',
height: '186 cm',
weight: '86 kg',
debut: 2011,
position: ['defender'],
games: 85,
goals: 11
},
{
number: 2,
pic: 'img/BELLCHAMBERS_T_t.png',
name: 'Tom',
surname: 'Bellchambers',
height: '202 cm',
weight: '106 kg',
debut: 2008,
position: ['ruck'],
games: 79,
goals: 53
},
{
number: 3,
pic: 'img/CHAPMAN_P_t.png',
name: 'Paul',
surname: 'Chapman',
height: '179 cm',
weight: '87 kg',
debut: 2000,
position: ['foward'],
games: 280,
goals: 366,
goals15: 8
},
];
etc etc
I have different functions to create a listQuery from the Squad_List based on different positions, games played etc ie addListDefender creates a list of players whose position = defender
I have a drawtable function to write the info to the page
I've got a select menu to pick the different listQuery options the values named after the relevant listQuery function it should call
App.js
var listQuery = [];
// Draw table from 'listQuery' array of objects
function drawTable(tbody) {
var tr, td;
tbody = document.getElementById(tbody);
// loop through data source
// document.write(tbody);
for (var i = 0; i < listQuery.length; i++) {
tr = tbody.insertRow(tbody.rows.length);
td = tr.insertCell(tr.cells.length);
td.setAttribute("align", "center");
td.innerHTML = "<p>" + listQuery[i].number + "</p>";
td = tr.insertCell(tr.cells.length);
td.innerHTML = '<img src="' + listQuery[i].pic + '">';
td = tr.insertCell(tr.cells.length);
td.innerHTML = listQuery[i].name + " " + listQuery[i].surname;
td = tr.insertCell(tr.cells.length);
td.innerHTML = listQuery[i].height;
td = tr.insertCell(tr.cells.length);
td.innerHTML = listQuery[i].weight;
td = tr.insertCell(tr.cells.length);
td.innerHTML = listQuery[i].debut;
td = tr.insertCell(tr.cells.length);
if (listQuery[i].position.length > 1) {
td.innerHTML += listQuery[i].position[0] + " / " + listQuery[i].position[1];
} else {
td.innerHTML += listQuery[i].position;
}
td = tr.insertCell(tr.cells.length);
td.innerHTML = listQuery[i].games;
td = tr.insertCell(tr.cells.length);
td.innerHTML = listQuery[i].goals;
td = tr.insertCell(tr.cells.length);
}
}
//Display entire list
var displayList = function() {
listQuery = squad;
};
//Take players from list that position = foward
var addListFoward = function() {
for (i = 0; i < squad.length; i++) {
if (squad[i].position.indexOf("foward") >= 0) {
listQuery.push(squad[i]);
console.log(squad[i]);
}
}
}
//Take players from list whose position = defender
var addListDefender = function() {
for (i = 0; i < squad.length; i++) {
if (squad[i].position === "defender") {
listQuery.push(squad[i]);
console.log(squad[i]);
}
}
}
//Take 10 items from player list in order of most games
var addListGames = function () {
squad.sort(function(a, b){
return b.games-a.games
})
listQuery = squad;
listQuery.length = 10;
}
// Site starts with Display Entire List
displayList();
drawTable("output");
//Generate list query on change select button from select options value
$('#select').change(function() {
});
//display selection from select button onclick go button
$('#go').click(function() {
// alert("go has been click!");
drawTable("output");
});
Basically when the select menu is changed I want to call a function equal to the select value and then redraw the table with the go button.....but the various things I've tried on $('#select') and $('#go') don't work.
Any help much appreciated!!!
<!DOCTYPE html>
<html lang="en">
<head>
<meta cahrset="UTF-8">
<title>Bombers Squad</title>
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<div class="header">
<div class="main-title">
<h1>Bombers Squad</h1>
</div>
<div class="logo">
<img src="img/logo-2x.png" alt="Bombers logo">
</div>
</div>
<div class="main-field">
<form>
<label for="select">View Bombers Squad:</label>
<select id="select" name="bombers_list">
<option value="displayList">Display Entire List</option>
<option value="addListFoward">Display Fowards</option>
<option value="addListMidfield">Display Midfielders</option>
<option value="addListDefender">Display Defenders</option>
<option value="addListRuck">Display Rucks</option>
<option value="addListGames">Display Most Games</option>
<option value="addGoals2015">2015 Goal kickers</option>
<option value="addBF2015">2015 Best & Fairest Votes</option>
</select>
<button id="go" type="submit">Go</button>
</form>
<table id="players">
<caption>Player Information</caption>
<thead>
<tr>
<th scope="col">Number</th>
<th scope="col">Picture</th>
<th scope="col">Name</th>
<th scope="col">Height</th>
<th scope="col">Weight</th>
<th scope="col">Debut</th>
<th scope="col">Position</th>
<th scope="col">Games</th>
<th scope="col">Goals</th>
</tr>
</thead>
<tbody id="output">
</tbody>
</table>
</div>
<script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="js/squad_list.js"></script>
<script src="js/app.js"></script>
</body>
</html>
JsFidder
Seems you just forget to include jquery or not referenced properly
check the below Answer & working demo
or simply add
$(function(){
$('#select').change(function () {
});
//display selection from select button onclick go button
$('#go').click(function () {
alert("go has been click!");
drawTable("output");
});
})

Categories

Resources