I have the next HTML code:
<table border="1">
<tr>
<th>Nº</th>
<th>NOMBRE</th>
<th>AREA</th>
</tr>
<tbody id="curso">
<tr>
<td>1</td>
<td>Jose</td>
<td>Gramática</td>
</tr>
<tr>
<td>2</td>
<td>Luis</td>
<td>Gramática</td>
</tr>
<tr>
<td>3</td>
<td>Andrea</td>
<td>Gramática</td>
</tr>
<tr>
<td>4</td>
<td>Andrea</td>
<td>Idiomas</td>
</tr>
</tbody>
</table>
<p id="Cantidad"></p>
</body>
This code can count the number of rows using a criteria in the AREA field:
<script>
function ContarFila() {
var cantidadFilas = document.getElementById("curso").rows.length;
let resultados = {};
let elementos = document.querySelectorAll(
"table tbody tr > td:nth-child(3)"
);
elementos.forEach(elemento => {
if (resultados.hasOwnProperty(elemento.innerText)) {
resultados[elemento.innerText]++;
} else {
resultados[elemento.innerText] = 1;
}
});
console.log(resultados);
for (let indice in resultados) {
document.getElementById("Cantidad").innerHTML =
resultados['Gramática'];
};
}
window.onload=ContarFila();
</script>
When loading the page and using the 'Gramática' criteria of the 'AREA' field, the result is 3.
How do I include MULTIPLE CRITERIA with the 'NOMBRE' and 'AREA' field at the same time?
Example:
Using Criteria 1: 'Grammar'
Using Criteria 2: 'Luis'
RESULT: 1
THAK YOU!
If I understood you correctly, then perhaps this example below can help you:
function getResult(table, search = {}) {
const tableHeadCells = Array.from(table.tHead.rows[0].cells);
const tableBodyRows = Array.from(table.tBodies[0].rows);
const indexes = tableHeadCells.map((el) => el.dataset["label"]); // <= // ['index', 'nombre', 'area']
const count = tableBodyRows.reduce((acc, row) => {
isSearched = indexes.every(
(key, i) => !search[key] || search[key] === row.cells[i].innerText
);
return isSearched ? ++acc : acc;
}, 0);
return count;
}
const table = document.querySelector('table');
console.log('1 => ', getResult(table, {
area: 'Gramática'
}));
console.log('2 => ', getResult(table, {
nombre: 'Luis',
area: 'Gramática'
}));
console.log('3 => ', getResult(table, {
index: '3',
nombre: 'Andrea'
}));
<table border="1">
<thead>
<tr>
<th data-label="index">Nº</th>
<th data-label="nombre">NOMBRE</th>
<th data-label="area">AREA</th>
</tr>
</thead>
<tbody id="curso">
<tr>
<td>1</td>
<td>Jose</td>
<td>Gramática</td>
</tr>
<tr>
<td>2</td>
<td>Luis</td>
<td>Gramática</td>
</tr>
<tr>
<td>3</td>
<td>Andrea</td>
<td>Gramática</td>
</tr>
<tr>
<td>4</td>
<td>Andrea</td>
<td>Idiomas</td>
</tr>
</tbody>
</table>
Note that in the header of the table, the columns are indicated by data-attributes data-label in order to understand which column belongs to which keyword. And then, we just count the number of elements in the loop, which completely match the specified requirements for all columns
Related
I need to sort these two tables in the same function. When I click name in first table so it will sort second table also by name.
I have this function where can sort table and it is working, but it only sorts one table.
What changes are needed to sort both tables?
function sortTable(table, column, asc = true) {
const dirModifier = asc ? 1 : -1;
const tBody = table.tBodies[0];
const rows = Array.from(tBody.querySelectorAll("tr"));
const sortedRows = rows.sort((a, b) => {
const aColText = a.querySelector(`td:nth-child(${ column + 1 })`).textContent.trim();
const bColText = b.querySelector(`td:nth-child(${ column + 1 })`).textContent.trim();
return aColText > bColText ? (1 * dirModifier) : (-1 * dirModifier);
});
while (tBody.firstChild) {
tBody.removeChild(tBody.firstChild);
}
tBody.append(...sortedRows);
table.querySelectorAll("th").forEach(th => th.classList.remove("th-sort-asc", "th-sort-desc"));
table.querySelector(`th:nth-child(${ column + 1})`).classList.toggle("th-sort-asc", asc);
table.querySelector(`th:nth-child(${ column + 1})`).classList.toggle("th-sort-desc", !asc);
}
document.querySelectorAll(".table-sortable th").forEach(headerCell => {
headerCell.addEventListener("click", () => {
const tableElement = headerCell.parentElement.parentElement.parentElement;
const headerIndex = Array.prototype.indexOf.call(headerCell.parentElement.children, headerCell);
const currentIsAscending = headerCell.classList.contains("th-sort-asc");
sortTable(tableElement, headerIndex, !currentIsAscending);
});
});
<table class="table-sortable">
<thead>
<tr>
<th>name</th>
<th>adress</th>
<th>Age</th>
</tr>
</thead>
<tbody>
<tr>
<td>Tom</td>
<td>Oslo</td>
<td>35</td>
</tr>
<tr>
<td>Per</td>
<td>London</td>
<td>29</td>
</tr>
<tr>
<td>Hary</td>
<td>Madrid</td>
<td>30</td>
</tr>
</tbody>
</table>
<table class="table-sortable">
<thead>
<tr>
<th>name</th>
<th>adress</th>
<th>Age</th>
</tr>
</thead>
<tbody>
<tr>
<td>Tom</td>
<td>Oslo</td>
<td>35</td>
</tr>
<tr>
<td>Per</td>
<td>London</td>
<td>29</td>
</tr>
<tr>
<td>Hary</td>
<td>Madrid</td>
<td>30</td>
</tr>
</tbody>
</table>
You can run sortTable function for each table.
var tables =document.getElementsByClassName("table-sortable");
sortTable(tables[0], headerIndex, !currentIsAscending);
sortTable(tables[1], headerIndex, !currentIsAscending);
function sortTable(table, column, asc = true) {
const dirModifier = asc ? 1 : -1;
const tBody = table.tBodies[0];
const rows = Array.from(tBody.querySelectorAll("tr"));
const sortedRows = rows.sort((a, b) => {
const aColText = a.querySelector(`td:nth-child(${ column + 1 })`).textContent.trim();
const bColText = b.querySelector(`td:nth-child(${ column + 1 })`).textContent.trim();
return aColText > bColText ? (1 * dirModifier) : (-1 * dirModifier);
});
while (tBody.firstChild) {
tBody.removeChild(tBody.firstChild);
}
tBody.append(...sortedRows);
table.querySelectorAll("th").forEach(th => th.classList.remove("th-sort-asc", "th-sort-desc"));
table.querySelector(`th:nth-child(${ column + 1})`).classList.toggle("th-sort-asc", asc);
table.querySelector(`th:nth-child(${ column + 1})`).classList.toggle("th-sort-desc", !asc);
}
document.querySelectorAll(".table-sortable th").forEach(headerCell => {
headerCell.addEventListener("click", () => {
const tableElement = headerCell.parentElement.parentElement.parentElement;
const headerIndex = Array.prototype.indexOf.call(headerCell.parentElement.children, headerCell);
const currentIsAscending = headerCell.classList.contains("th-sort-asc");
var tables =document.getElementsByClassName("table-sortable");
sortTable(tables[0], headerIndex, !currentIsAscending);
sortTable(tables[1], headerIndex, !currentIsAscending);
});
});
<table class="table-sortable">
<thead>
<tr>
<th>name</th>
<th>adress</th>
<th>Age</th>
</tr>
</thead>
<tbody>
<tr>
<td>Tom</td>
<td>Oslo</td>
<td>35</td>
</tr>
<tr>
<td>Per</td>
<td>London</td>
<td>29</td>
</tr>
<tr>
<td>Hary</td>
<td>Madrid</td>
<td>30</td>
</tr>
</tbody>
</table>
<table class="table-sortable">
<thead>
<tr>
<th>name</th>
<th>adress</th>
<th>Age</th>
</tr>
</thead>
<tbody>
<tr>
<td>Tom</td>
<td>Oslo</td>
<td>35</td>
</tr>
<tr>
<td>Per</td>
<td>London</td>
<td>29</td>
</tr>
<tr>
<td>Hary</td>
<td>Madrid</td>
<td>30</td>
</tr>
</tbody>
</table>
I am trying to sort the table by total value. Link. This works but it fails when two table elements have same point.
I am trying to sort the table using javascript to order by total points at the end. The table is a dynamic one so W1, W2, W3 columns adds up to total. Each row is dynamically created as well.
Please help
const sortTotal = () => {
const tbl = [...document.getElementsByClassName("fl-table")][0];
const tbody = [...tbl.tBodies][0];
const oObjects = [];
[...tbody.rows].forEach(row => {
const cells = [...row.cells];
const obj = [...row.cells].map(cell => {
return cell.innerHTML;
});
oObjects.push(obj);
});
oObjects.sort((a, b) => a[a.length -2] > b[b.length -2] ? 1 : -1);
[...tbody.rows].forEach((row, i) => {
[...row.cells].forEach((cell, j) => {
cell.innerHTML = oObjects[i][j];
});
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button onClick="sortTotal()">
Sort</button>
</button>
<table class="fl-table">
<thead>
<tr>
<th>Player</th>
<th>Player Name</th>
<th>W1</th>
<th>W2</th>
<th>Total</th>
</tr>
</thead>
<tbody>
<tr>
<td class="agl-profile-img-new"><img src="https://via.placeholder.com/70C/O https://placeholder.com/"></td>
<td>Heather Rankin</td>
<td>4</td>
<td>21</td>
<td>25</td>
</tr>
<tr>
<td class="agl-profile-img-new"><img src="https://via.placeholder.com/70C/O https://placeholder.com/"></td>
<td>Stephen Puopolo</td>
<td>3</td>
<td>1</td>
<td>4</td>
</tr>
<tr>
<td class="agl-profile-img-new"><img src="https://via.placeholder.com/70C/O https://placeholder.com/"></td>
<td>Latheesh V M V</td>
<td>2</td>
<td>26</td>
<td>4</td>
</tr>
</tbody>
</table>
You are sorting by wrong column.
a[a.length -2] > b[b.length -2]
should be
a[a.length -1] > b[b.length -1]
and sort will make 25, 4, 4 by total
I am trying to sort the table using javascript to order by total points at the end. The table is a dynamic one so W1, W2, W3 columns adds up to total. Is there way to order rows them by total in javascript. Each row is dynamically created as well.
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table class="fl-table">
<thead>
<tr>
<th>Player</th>
<th>Player Name</th>
<!-- <th>W1</th>
<th>W2</th> -->
<th>W1</th>
<th>W2</th>
<th>Total</th>
</tr>
</thead>
<tbody>
<tr>
<td class="agl-profile-img-new"><img src="https://via.placeholder.com/70
C/O https://placeholder.com/"></td>
<td>Heather Rankin</td>
<td>4</td>
<td>21</td>
<td>25</td>
</tr>
<tr>
<td class="agl-profile-img-new"><img src="https://via.placeholder.com/70
C/O https://placeholder.com/"></td>
<td>Stephen Puopolo</td>
<td>3</td>
<td>1</td>
<td>4</td>
</tr>
<tr>
<td class="agl-profile-img-new"><img src="https://via.placeholder.com/70
C/O https://placeholder.com/"></td>
<td>Latheesh V M V</td>
<td>2</td>
<td>26</td>
<td>28</td>
</tr>
</tbody>
<tbody>
</tbody>
</table>
Is there is any way? Please help
can you gather the cells into object and sort them like this. https://jsfiddle.net/e4oscnz8/4/
const sortTotal = () => {
const tbl = [...document.getElementsByClassName("fl-table")][0];
const tbody = [...tbl.tBodies][0];
const oObjects = [];
[...tbody.rows].forEach(row => {
const cells = [...row.cells];
const obj = [...row.cells].map(cell => {
return cell.innerHTML;
});
oObjects.push(obj);
});
oObjects.sort((a, b) => a[a.length -2] > b[b.length -2] ? 1 : -1);
[...tbody.rows].forEach((row, i) => {
[...row.cells].forEach((cell, j) => {
cell.innerHTML = oObjects[i][j];
});
});
}
push tr rows into array or object and sort by your custom sort function: https://jsfiddle.net/2dq7m8k9/
you are using jquery so life is good :)
function SortByTotal(tr1, tr2){//descending sorting
var total1 = parseInt(tr1.find('td:last-child').text());
var total2 = parseInt(tr2.find('td:last-child').text());
return ((total1 > total2) ? -1 : ((total1 < total2) ? 1 : 0));
}
var trs=new Array();
$('#mytable tbody').first().children('tr').each(function(){
trs.push($(this).clone());
});
trs.sort(SortByTotal);
$('#mytable tbody').first().empty();
var i=0;
for (i = 0; i < trs.length; i++) {
$('#mytable tbody').first().append(trs[i]);
}
I am creating a table where in it has too many data and I want to put select option and just filter it, I already got the filter thing in the table using select, my question is how can I not manually add options to the select?
for example I have a column of dates containing 5 - 01-12-19 , 2 - 01-13-19 and 10 - 01-14-19 the said dates need to be the one in the options, and should only display 3 dates which is 01-12-19,01-13-19,01-14-19, the thing with manually adding options is not the best option because the column of dates are updated day by day.
<select>
<option></option>
</select>
<table border="2">
<thead>
<tr>
<th>Name</th>
<th>Dates</th>
</tr>
</thead>
<tbody>
<tr>
<td>A</td>
<td>01-12-19</td>
</tr>
<tr>
<td>Jas</td>
<td>01-12-19</td>
</tr>
<tr>
<td>sam</td>
<td>01-12-19</td>
</tr>
<tr>
<td>James</td>
<td>01-12-19</td>
</tr>
<tr>
<td>luke</td>
<td>01-13-19</td>
</tr>
<tr>
<td>lucas</td>
<td>01-13-19</td>
</tr>
<tr>
<td>laus</td>
<td>01-13-19</td>
</tr>
<tr>
<td>cals</td>
<td>01-13-19</td>
</tr>
<tr>
<td>miya</td>
<td>01-14-19</td>
</tr><tr>
<td>lesley</td>
<td>01-14-19</td>
</tr>
</tbody>
</table>
I expect that using javascript the options will automatically fillout by the available dates but will only display in option as one even if there are 10 dates that are the same.
Assuming you're just trying to populate your select option with one of the columns then filter the below table every-time it changes, something like this will work.
const table = document.getElementById('myTable');
const trs = table.querySelectorAll('tbody tr');
const getAllDatesInTable = () => {
const table = document.getElementById('myTable');
const trs = table.querySelectorAll('tbody tr');
const dates = [];
trs.forEach( tr => {
const date = tr.querySelector('td:last-child').innerText;
if (!dates.includes(date)) {
dates.push(date);
}
});
return dates;
};
const dates = getAllDatesInTable();
const select = document.getElementById('dateFilter');
select.innerHTML = dates.map( d => `<option value=${d}>${d}</option>`);
select.onchange = (e) => {
const selection = e.target.value;
trs.forEach( tr => {
tr.style.display = '';
const date = tr.querySelector('td:last-child').innerText;
if (date !== selection) {
tr.style.display = 'none';
}
});
};
<select id="dateFilter">
<option></option>
</select>
<table border="2" id="myTable">
<thead>
<tr>
<th>Name</th>
<th>Dates</th>
</tr>
</thead>
<tbody>
<tr>
<td>A</td>
<td>01-12-19</td>
</tr>
<tr>
<td>Jas</td>
<td>01-12-19</td>
</tr>
<tr>
<td>sam</td>
<td>01-12-19</td>
</tr>
<tr>
<td>James</td>
<td>01-12-19</td>
</tr>
<tr>
<td>luke</td>
<td>01-13-19</td>
</tr>
<tr>
<td>lucas</td>
<td>01-13-19</td>
</tr>
<tr>
<td>laus</td>
<td>01-13-19</td>
</tr>
<tr>
<td>cals</td>
<td>01-13-19</td>
</tr>
<tr>
<td>miya</td>
<td>01-14-19</td>
</tr><tr>
<td>lesley</td>
<td>01-14-19</td>
</tr>
</tbody>
</table>
I've got a table with 5 rows and two columns. Each row, has an ID column, ranging from 1-5.
I want to add JSON data to that said table, IF, that data has a matching ID to that row. If NO data matches that rows ID, add "No Matching Record" to that rows second column.
HTML Table
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>Lastname</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td></td>
</tr>
<tr>
<td>2</td>
<td></td>
</tr>
<tr>
<td>3</td>
<td></td>
</tr>
<tr>
<td>4</td>
<td></td>
</tr>
<tr>
<td>5</td>
<td></td>
</tr>
</tbody>
</table>
Json Data
{"data":[
{"id":"1", "lastName":"Doe"},
{"id":"3", "lastName":"Jones"}
]}
Expected Result
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>Lastname</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Doe</td>
</tr>
<tr>
<td>2</td>
<td>No Matching Record</td>
</tr>
<tr>
<td>3</td>
<td>Jones</td>
</tr>
<tr>
<td>4</td>
<td>No Matching Record</td>
</tr>
<tr>
<td>5</td>
<td>No Matching Record</td>
</tr>
</tbody>
</table>
You can do this with .each() to loop each tr and then use find() to get object from data that has same id as text in td.
//Loop each row or tr
$('tbody tr').each(function() {
//Get text or number from each first td in every row
var i = $(this).find('td:first').text();
//Find object from data with this id or current id of td
var r = data.data.find((e) => e.id == i);
//Select second td from current row
var t = $(this).find('td:eq(1)');
//If Object is found with current id add lastName as text else add dummy text or No Matching Record
(r != undefined) ? t.text(r.lastName): t.text('No Matching Record');
});
var data = {"data":[{"id":"1", "lastName":"Doe"},{"id":"3", "lastName":"Jones"}]}
$('tbody tr').each(function() {
var i = $(this).find('td:first').text();
var r = data.data.find((e) => e.id == i);
var t = $(this).find('td:eq(1)');
(r != undefined) ? t.text(r.lastName): t.text('No Matching Record');
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>Lastname</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td></td>
</tr>
<tr>
<td>2</td>
<td></td>
</tr>
<tr>
<td>3</td>
<td></td>
</tr>
<tr>
<td>4</td>
<td></td>
</tr>
<tr>
<td>5</td>
<td></td>
</tr>
</tbody>
</table>
If you want to filter by index of rows instead of text from td you can just use $(this).index() + 1; and the rest is same
var data = {
"data": [{
"id": "1",
"lastName": "Doe"
}, {
"id": "3",
"lastName": "Jones"
}, ]
}
//Loop each row or tr
$('tbody tr').each(function() {
//Get index of row
var i = $(this).index() + 1;
//Find object from data with this id or current id of td
var r = data.data.find((e) => e.id == i);
//Select second td from current row
var t = $(this).find('td:eq(1)');
//If Object is found with current id add lastName as text else add dummy text or No Matching Record
(r != undefined) ? t.text(r.lastName): t.text('No Matching Record');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>Lastname</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td></td>
</tr>
<tr>
<td>2</td>
<td></td>
</tr>
<tr>
<td>3</td>
<td></td>
</tr>
<tr>
<td>4</td>
<td></td>
</tr>
<tr>
<td>5</td>
<td></td>
</tr>
</tbody>
</table>
First, add classes to the two types of td's for convenience. Then following code should work. Here I am iterating through all rows in tbody and then searching through the json if any matching value is found. If no matching value is found, default value ("No data found") is put in the lastName column.
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>Lastname</th>
</tr>
</thead>
<tbody>
<tr>
<td class="id">1</td>
<td class="name"></td>
</tr>
<tr>
<td class="id">2</td>
<td class="name"></td>
</tr>
<tr>
<td class="id">3</td>
<td class="name"></td>
</tr>
<tr>
<td class="id">4</td>
<td class="name"></td>
</tr>
<tr>
<td class="id">5</td>
<td class="name"></td>
</tr>
</tbody>
</table>
var json = {"data":[
{"id":"1", "lastName":"Doe"},
{"id":"3", "lastName":"Jones"}
]};
$(".table tbody tr").each(function(index){
var curId = $(this).find(".id").text();
var nameField = "No data found";
for( var i = 0; i < json.data.length; i++ )
{
var row = json.data[i];
if( row.id == curId )
{
nameField = row.lastName;
return false;
}
}
$(this).find(".name").text( nameField );
});//each
The following code would do the trick:
var response = {"data":[
{"id":"1", "lastName":"Doe"},
{"id":"3", "lastName":"Jones"}
]};
var myData = response.data;
var rows = $('#myDataTable tbody tr');
var cells, index, itemFound = false;
rows.each(function (index) {
cells = $(this).find('td');
itemFound = false
for (index=myData.length-1 ; index>= 0 && !itemFound; index--) {
if (cells.eq(0).text() == myData[index].id) {
itemFound = true;
cells.eq(1).text(myData[index].lastName);
myData.splice(index, 1);
}
}
if (!itemFound) {
cells.eq(1).text('No matching record');
}
});
See my working js fiddle:
https://jsfiddle.net/gkptnnxe/
If you don't want to add class or id's to your td's then you can use this.
var obj = {"data":[
{"id":"1", "lastName":"Doe"},
{"id":"3", "lastName":"Jones"}
]};
var trs = document.getElementsByTagName("tr");
// don't need for i==0
for(var i=1; i<trs.length; i++){
var tds = trs[i].children;
var id = tds[0].innerHTML;
var nameFound = false;
//search this id in json.
var len = obj.data.length;
for(var j=0; j<obj.data.length; j++){
if(obj.data[j].id == id){
// If found then change the value of this lastName cell.
tds[1].innerHTML = obj.data[j].lastName;
nameFound = true;
}
}
// If id is not found is json then set the default message.
if(nameFound == false){
tds[1].innerHTML = "No mathcing records";
}
}
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>Lastname</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td></td>
</tr>
<tr>
<td>2</td>
<td></td>
</tr>
<tr>
<td>3</td>
<td></td>
</tr>
<tr>
<td>4</td>
<td></td>
</tr>
<tr>
<td>5</td>
<td></td>
</tr>
</tbody>
</table>
You can get td by index and check the text of td if it matches add name to next td
var data = {
"data": [
{ "id": "1", "lastName": "Doe" },
{ "id": "3", "lastName": "Jones" }
]
};
$(".table-striped tbody tr").each(function(){
var index = data.data.map(function (e) { return e.id }).indexOf($(this).first().text().trim());
if(index > -1)
$(this).children('td:eq(1)').text(data.data[index].lastName);
else
$(this).children('td:eq(1)').text('No matching record');
});