Get summary number from only visible table rows - javascript

I have a code which counts a total summary of all price table cells. This is not exactly what do I need. I need to count the summary from only visible rows. I use filtering by date range, once I change date it displays only dates from the selected date range, but the summary price is from the all rows. I need to implement the part of the code for visible table rows only and update the summary price in the span.
Any advice?
function filterRows() {
var from = jQuery("#datefilterfrom").val();
var to = jQuery("#datefilterto").val();
if (!from && !to) { // no value for from and to
return;
}
from = from || "1970-01-01"; // default from to a old date if it is not set
to = to || "2999-12-31";
var dateFrom = moment(from);
var dateTo = moment(to);
jQuery("#table tr").each(function(i, tr) {
var val = jQuery(tr).find("td:nth-child(2)").text();
var dateVal = moment(val, "DD.MM.YYYY");
var visible = (dateVal.isBetween(dateFrom, dateTo, null, [])) ? "" : "none"; // [] for inclusive
jQuery(tr).css("display", visible);
//summary start
var table = document.getElementById("table"),
sumVal = 0;
for (var i = 1; i < table.rows.length; i++) {
sumVal = sumVal + parseInt(table.rows[i].cells[2].innerHTML);
}
document.getElementById("val").innerHTML = "Sum Value = " + sumVal;
console.log(sumVal);
});
//summary end
}
jQuery("#datefilterfrom").on("change", filterRows);
jQuery("#datefilterto").on("change", filterRows);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js"></script>
<div class="row">
<div class="col-md-3">
<h4>Date from</h4>
<input type="date" class="form-control" id="datefilterfrom" data-date-split-input="true">
<h4>Date to</h4>
<input type="date" class="form-control" id="datefilterto" data-date-split-input="true">
</div>
</div>
<span id="val"></span>
<table id="table" class="sortable">
<tr>
<th>Name</th>
<th>Date</th>
<th>Price</th>
</tr>
<tr>
<td>Name</td>
<td>20.10.2020</td>
<td>20</td>
</tr>
<tr>
<td>Name</td>
<td>21.10.2020</td>
<td>25</td>
</tr>
<tr>
<td>Name</td>
<td>22.10.2020</td>
<td>30</td>
</tr>
</table>
This is how it looks like when I select date range in HTML (so I need to get summary from only this selected tr)
I think there need to be some condition added here
sumVal = sumVal + parseInt(table.rows[i].cells[2].innerHTML);

You don't need two loops. You're already looping through the rows with .each() to make them visible or invisible, you can calculate the total in that same loop. After you determine if the row should be visible, use that variable in an if statement to add to the total.
jQuery has a built-in function .toggle() that will switch the visibility of an element. You can make visible a boolean variable instead of a display style value, then use that as the argument to .toggle(). Then you can use this same value in the if condition.
Make your access to columns less dependent on the table layout by using classes instead of column indexes. Use jQuery(tr).find(".price") to access the price column, for instance.
Use <thead> and <tbody> to distinguish the headings from the table data, then use tbody in the .each() loop to only process data rows.
function filterRows() {
var from = jQuery("#datefilterfrom").val();
var to = jQuery("#datefilterto").val();
if (!from && !to) { // no value for from and to
return;
}
from = from || "1970-01-01"; // default from to a old date if it is not set
to = to || "2999-12-31";
var dateFrom = moment(from);
var dateTo = moment(to);
var sumVal = 0;
jQuery("#table tbody tr").each(function(i, tr) {
var val = jQuery(tr).find(".date").text();
var dateVal = moment(val, "DD.MM.YYYY");
var visible = (dateVal.isBetween(dateFrom, dateTo, null, []));
jQuery(tr).toggle(visible);
if (visible) {
sumVal += parseInt(jQuery(tr).find(".price").text());
}
$("#val").text("Sum Value = " + sumVal);
});
}
jQuery("#datefilterfrom, #datefilterto").on("change", filterRows);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js"></script>
<div class="row">
<div class="col-md-3">
<h4>Date from</h4>
<input type="date" class="form-control" id="datefilterfrom" data-date-split-input="true">
<h4>Date to</h4>
<input type="date" class="form-control" id="datefilterto" data-date-split-input="true">
</div>
</div>
<span id="val"></span>
<table id="table" class="sortable">
<thead>
<tr>
<th>Name</th>
<th>Date</th>
<th>Price</th>
</tr>
<thead>
<tbody>
<tr>
<td>Name</td>
<td class="date">20.10.2020</td>
<td class="price">20</td>
</tr>
<tr>
<td>Name</td>
<td class="date">21.10.2020</td>
<td class="price">25</td>
</tr>
<tr>
<td>Name</td>
<td class="date">22.10.2020</td>
<td class="price">30</td>
</tr>
<tbody>
</table>

Related

How to search based on multiple column using basic jQuery

I am working on table layout with search on top of each row.
https://codepen.io/chiragjain94/pen/rNLBbNG
I want to search for "897" in first column and in second column I want to enter "Card"
The result should be => 553492-987 897 Traveller Card
But that is not the result I get.
$(document).on('input', '.filter-table', function() {
var searchKey = $(this).val().toLowerCase();
var tableName = $(this).attr('data-table-id');
var columnId = $(this).attr('data-column-id');
$("#" + tableName + " tbody td:nth-child(" + columnId + ")").filter(function() {
$(this).parent().toggle($(this).text().toLowerCase().indexOf(searchKey) > -1);
});
});
<table class="table mb-0 table-striped" id="selectedList">
<thead>
<tr>
<td colspan="2">Table</td>
</tr>
<tr class="search-fields" style="">
<td><input type="search" class="form-control search-input filter-table valid" data-table-id="selectedList" data-column-id="1" placeholder="Search column 1..." autocomplete="off"></td>
<td><input type="search" class="form-control search-input filter-table" data-table-id="selectedList" data-column-id="2" placeholder="Search column 2" autocomplete="off"></td>
</tr>
</thead>
<tbody>
<tr class="search-row">
<td>Card 126 897</td>
<td>Big Bank 2015</td>
</tr>
<tr class="search-row">
<td>553492-987 897</td>
<td>Traveller Card</td>
</tr>
<tr class="search-row" style="">
<td>Card 67920</td>
<td>Credit Card 8729
</td>
</tr>
</tbody>
</table>
Additional condition, once I clear any of the search input, my result table should still show result based on other search input.
Instead of filtering by comparing each input value separately, you should take both in account. You can do it this way:
$(document).on('input', '.filter-table', function () {
var tableName = $(this).attr('data-table-id');
var searchKey1 = $("[data-column-id='1']").val().toLowerCase();
var searchKey2 = $("[data-column-id='2']").val().toLowerCase();
$("#" + tableName + " tbody tr").filter(function () {
var columnSearch1 = !searchKey1 ||
$(this).children().eq(0).text().toLowerCase().indexOf(searchKey1) > -1;
var columnSearch2 = !searchKey2 ||
$(this).children().eq(1).text().toLowerCase().indexOf(searchKey2) > -1;
$(this).toggle(columnSearch1 && columnSearch2);
});
});
Link to example

getting error after executing the code TypeError: display is nullscript.js:22:22

HTML
This a HTML page I made to add row in the tables one by one below one another when user clicks on 'add' button and gives me error as below.
<div id="adder">
<table id="adderhd">
<tr>
<th>PARTICULARS</th>
<th>Amount</th>
</tr>
<tr>
<th><input type="text" id="particulars" value=""></th>
<th><input type="number" id="amount" value=""><button id="add">Add</button></th>
</tr>
</table>
</div>
<div id="tables">
<div>
<table class="table-1">
<tr>
<th>
PARTICULARS
</th>
<th>
AMOUNT
</th>
</tr>
</table>
</div>
</div>
JavaScript
This is a JavaScript code I wrote for it but after executing this I got error on console.
Please help me I am a beginner in JavaScript.
function rowAdder() {
var particulars = document.getElementById("particulars").value;
var amount = document.getElementById("amount").value;
if (!particulars || !amount) {
alert("Please enter the values inside all the field.");
return;
}
var display = document.getElementById("table-1");
var newRow = display.insertRow(row); //not working (insert(row));
var cel1 = newRow.insertCell(0);
var cel2 = newRow.insertCell(1);
cel1.innerHTML = particulars;
cel2.innerHTML = amount;
row++;
}
You need to add an event onclick on the button to call rowAdder function. Variable 'display' is getting element by id, so assign id="table-1" to your html element . Its working fine.
function rowAdder() {
var particulars = document.getElementById("particulars").value;
var amount = document.getElementById("amount").value;
if (!particulars || !amount) {
alert("Please enter the values inside all the field.");
return;
}
var display = document.getElementById("table-1");
var newRow = display.insertRow();
var cel1 = newRow.insertCell(0);
var cel2 = newRow.insertCell(1);
cel1.innerHTML = particulars;
cel2.innerHTML = amount;
}
<div id="adder">
<table id="adderhd">
<tr>
<th>PARTICULARS</th>
<th>Amount</th>
</tr>
<tr>
<th><input type="text" id="particulars" value=""></th>
<th><input type="number" id="amount" value="">
<button onclick="rowAdder()"id="add">Add</button></th>
</tr>
</table>
</div>
<div id="tables">
<div>
<table id="table-1">
<tr>
<th>
PARTICULARS
</th>
<th>
AMOUNT
</th>
</tr>
</table>
</div>
</div>

how to get dynamically generated table td input value

I have a dynamically generated table like below
this is the code that generate this table
function pullInventory(data) {
var container = document.getElementById('inventoryContainer')
var index = 0;
console.log(index)
data.forEach(function(awardsSnap) {
index ++;
// console.log(awardsSnap, index)
var awardItem = awardsSnap.val()
// Attach an asynchronous callback to rea
var NSNcard = `
<tr>
<td class="serial">${awardItem.NSN}</td>
<td> ${awardItem.Nomenclature} </td>
<td> ${awardItem.Awarddate} </td>
<td> ${awardItem.Awardid} </td>
<td>
<input type="text" placeholder="i.e. 100 EA" class="form-control" value="" id="qty${index}"style="width: 110px;">
</td>
<td>
<input type="text" placeholder="i.e. $9.23 " class="form-control" value="" style="width: 110px;">
</td>
</tr>
`;
container.innerHTML += NSNcard;
});
}
I want to get all the user entered quantity and price on a button click so I use this
document.querySelector("#savebtn").addEventListener("click", e => {
var rows = document.getElementById("WelcomeTable").getElementsByTagName("tbody")[0].getElementsByTagName("tr").length;
saveInventory(rows);
});
function saveInventory(rows) {
const columnHeader = Array.prototype.map.call(
document.querySelectorAll(".table th"),
th => {
return th.innerHTML;
}
);
const tableContent = Object.values(
document.querySelectorAll(".table tbody tr")
).map(tr => {
const tableRow = Object.values(tr.querySelectorAll("td")).reduce(
(accum, curr, i) => {
const obj = { ...accum };
obj[columnHeader[i]] = curr.innerHTML.trim();
console.log(accum, curr, i)
return obj;
},
{}
);
return tableRow;
});
}
everything works fine except that the two input column in the table above does not detect user input. I'm not able to get the quantity and price value entered.
Award Date: "08-23-2012"
Award#: "SP452013D0055"
NSN: "S222V00004789"
Nomenclature: " BATTERIES, NICKEL-CADMIUM"
Quantity: "<input type="text" placeholder="i.e. 100 EA" class="form-control" value="" id="qty18" style="width: 110px;">"
Unit-Price: "<input type="text" placeholder="i.e. $9.23 " class="form-control" value="" style="width: 110px;">"
I tried this and other things but they output undefine
obj[columnHeader[4]]=curr.val();
obj[columnHeader[4]]=curr.value;
how could i get the enetered quantity and price from the dynamic table?
You could try doing something like this:
window.onload = ()=>{
let targetTable = document.getElementById('target-table');
let targetTableRows = targetTable.rows;
let tableHeaders = targetTableRows[0];
// start from the second row as the first one only contains the table's headers
for(let i = 1; i < targetTableRows.length; i++){
// loop over the contents of each row
for(let j = 0; j < targetTableRows[i].cells.length; j++){
// something we could use to identify a given item
let currColumn = tableHeaders.cells[j].innerHTML;
// the current <td> element
let currData = targetTableRows[i].cells[j];
// the input field in the row
let currDataInput = currData.querySelector('input');
// is the current <td> element containing an input field? print its value.
// Otherwise, print whatever is insside
currDataInput ? console.log(`${currColumn}: ${currDataInput.value}`)
: console.log(`${currColumn}: ${currData.innerHTML}`);
}
}
};
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
<table class="table" id="target-table">
<thead>
<tr>
<th scope="col">Person #</th>
<th scope="col">First</th>
<th scope="col">Last</th>
<th scope="col">Handle</th>
<th scope="col">Quantity</th>
<th scope="col">Price</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">1</th>
<td>Mark</td>
<td>Otto</td>
<td>#mdo</td>
<td><input type="text" value="01-quantity" id="value-01"></td>
<td><input type="text" value="01-price" id="value-01-2"></td>
</tr>
<tr>
<th scope="row">2</th>
<td>Jacob</td>
<td>Thornton</td>
<td>#fat</td>
<td><input type="text" value="02-quantity" id="value-02"></td>
<td><input type="text" value="02-price" id="value-02-2"></td>
</tr>
<tr>
<th scope="row">3</th>
<td>Larry</td>
<td>the Bird</td>
<td>#twitter</td>
<td><input type="text" value="03-quantity" id="value-03"></td>
<td><input type="text" value="03-price" id="value-03-2"></td>
</tr>
</tbody>
</table>
What is done in the example above should also work for your specific case.
Also, here's a working exmaple :)
val() is jQuery method. You'll need to use .value in JavaScript.
obj[columnHeader[i]] = curr.innerHTML.trim();
innerHtml.trim returns only tag having direct child with text in it. In your code last two td having an input as child.
so in that case you need to check 'curr' having a child available. if there is a child available and its tagName is input, then you have to use childs value.
for example
obj[columnHeader[i]] = curr.children.length && curr.children[0].tagName=="INPUT" ? curr.children[0].value : curr.innerHTML.trim();
the above condition can be check and assign to a variable before it is assigned to key

JavaScript logic error

I am trying to get JavaScript to generate a table with data from an array for me however when I run this code it enters the same data twice. Does anyone know what mistake I am making?
The HTML:
<body>
<div class="container">
<h1 class="page-header">Bookings</h1>
<table id="bookTable" class="table table-bordered">
<thead>
<tr>
<th>First Name</th>
<th>Second Name</th>
<th>Email</th>
<th>From</th>
<th>To</th>
</tr>
<tr>
<td type="text" id="firstName"></td>
<td type="text" id="secondName"></td>
<td type="text" id="email"></td>
<td type="text"id="dateFrom"></td>
<td type="text"id="dateTo"></td>
</tr>
</thead>
</table>
</div>
The JavaScript is using a guide I found that I have adapted. In reality, the array comes from a python file that is fed in as a variable when Flask displays the template. I have used the same data that python prints out when looking at the contents of the variable that is passed.
<script>
var listOfBookings = [John`Doe`jd#gmail.com`03/01/2018`18/01/2018`John`Doe`jd#gmail.com`26/12/2017`27/12/2017]
var array = listOfBookings.split("`");
var count = 1
for (var i = 0; i < array.length; i++){
if (1%(i+1) == 0){
var firstName = array[i];
}
if (2%(i+1) == 0){
var secondName = array[i];
}
if (3%(i+1) == 0){
var email = array[i];
}
if (4%(i+1) == 0){
var dateFrom = array[i];
}
if (5%(i+1) == 0){
var dateTo = array[i];
}
// Addapted from http://talkerscode.com/webtricks/add-edit-and-
delete-rows-from-table-dynamically-using-javascript.php
if(count%5 == 0){
var table=document.getElementById("bookTable");
var table_len=(table.rows.length)-1;
var row = table.insertRow(table_len).outerHTML="<tr
id='row"+table_len+"'><td id='date_row"+table_len+"'>"+firstName+"</td><td
id='name_row"+table_len+"'>"+secondName+"</td><td
id='country_row"+table_len+"'>"+email+"</td><td
id='country_row"+table_len+"'>"+dateFrom+"</td><td
id='country_row"+table_len+"'>"+dateTo+"</td></tr>";
}
count += 1
}
</script>
Your code seems overly complicated. Here's a working example of how you might do it: (Note: I've added a tbody to your table, rather than have all these rows inserted into the thead)
var listOfBookings = "John`Doe`jd#gmail.com`03/01/2018`18/01/2018`John`Doe`jd#gmail.com`26/12/2017`27/12/2017";
var array = listOfBookings.split("`");
var count = 1;
var table = document.getElementById("bookTable").getElementsByTagName('tbody')[0];
var row = table.insertRow(-1); //appends initial row
row.id = "row_" + (count)
for (var i = 0; i < array.length; i++) {
if (i % 5 == 0 && i > 0) {
row = table.insertRow(-1); //appends a row
row.id = "row_" + (++count)
}
var cell = row.insertCell(-1); //appends a cell
var text = document.createTextNode(array[i]); //create the textNode
cell.appendChild(text); //fill the cell with the text
//(you could also set the cell id and other attributes at this point too)
}
<div class="container">
<h1 class="page-header">Bookings</h1>
<table id="bookTable" class="table table-bordered">
<thead>
<tr>
<th>First Name</th>
<th>Second Name</th>
<th>Email</th>
<th>From</th>
<th>To</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
Your array seems to have 10 values and from all your if statements none of them will have their condition equal true when i is bigger then 4.
All the variables (firstName, secondName, email, dataFrom, dateTo) will have the same value when count is 10 as they had when it was 5
You could do something like this:
var array = ["John","Doe","jd#gmail.com","03/01/2018","18/01/2018","John","Doe","jd#gmail.com","26/12/2017","27/12/2017"];
var table=document.getElementById("bookTable");
for (var i = 0; array.length % 5 === 0 && i < array.length;){
var firstName = array[i++];
var secondName = array[i++];
var email = array[i++];
var dateFrom = array[i++];
var dateTo = array[i++];
var table_len = (table.rows.length)-1;
var row = table.insertRow(table_len).outerHTML="<tr id='row"+table_len+"'><td id='date_row"+table_len+"'>"+firstName+"</td><td id='name_row"+table_len+"'>"+secondName+"</td><td id='country_row"+table_len+"'>"+email+"</td><td id='country_row"+table_len+"'>"+dateFrom+"</td><td id='country_row"+table_len+"'>"+dateTo+"</td></tr>";
}
<body>
<div class="container">
<h1 class="page-header">Bookings</h1>
<table id="bookTable" class="table table-bordered">
<thead>
<tr>
<th>First Name</th>
<th>Second Name</th>
<th>Email</th>
<th>From</th>
<th>To</th>
</tr>
<tr>
<td type="text" id="firstName"></td>
<td type="text" id="secondName"></td>
<td type="text" id="email"></td>
<td type="text"id="dateFrom"></td>
<td type="text"id="dateTo"></td>
</tr>
</thead>
</table>
</div>

JQuery issue using closest to find text inputs

I currently am outputting two tables in each iteration of a foreach loop (c# razor view although that isn't too relevant here). I wrap these two tables in a div with class = jq-roundContainer and each input in both tables has class jq-hitOrMiss. I am trying to sum up the number of X's entered into the text inputs as follows but variable sum is 0 (when I know it shouldnt be) and inputs.length is 0 also. html and simple jquery function below
html:
<div class="jq-roundContainer">
<table class="table table-bordered">
<thead>
<tr class="active">
<th colspan="2" class="text-center">1</th>
<th colspan="2" class="text-center">2</th>
</tr>
</thead>
<tbody>
<tr>
<td style="display:none">
#Html.Hidden("EventId", Request.Params["eventId"]);
#Html.Hidden("UserId", Request.Params["userId"]);
<input type="hidden" name="scoreCards[#i].UserProfile" value="#round.UserProfile" />
</td>
<td>
<input class="jq-hitOrMiss" onchange="SumHits();" name="scoreCards[#i].Hit1StationOneShotOne" pattern="[xXoO]" type="text" maxlength="1" size="1" value="#ScoreHitMisConverter.IsHitToTableRowValue(round.Hit1StationOneShotOne)" />
</td>
#{i++;}
</tr>
</tbody>
</table>
<table class="table table-bordered">
<thead>
<tr class="active">
<th colspan="2" class="text-center">14</th>
<th class="text-center">TOTAL</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<input class="jq-hitOrMiss" onchange="SumHits();" name="scoreCards[#i].Hit27StationThreeShotSeven" pattern="[xXoO]" type="text" maxlength="1" size="1" value="#ScoreHitMisConverter.IsHitToTableRowValue(round.Hit27StationThreeShotSeven)" />
</td>
<td class="text-center total jq-total">#round.Score</td>
#{i++;}
</tr>
</tbody>
</table>
</div>
and jquery function:
function SumHits() {
var sum = 0;
var inputs = $(this).closest('.jq-roundContainer').find('.jq-hitOrMiss');
$.each(inputs, function (index, value) {
var value = $(value).val();
if (value == 'X' || value == 'x') {
sum++;
}
});
var totalInput = $(this).closest('.jq-roundContainer').find('.jq-total');
totalInput.text(sum);
}
Inside normal function this will points to window. So when you are using an inline handler, you have to pass the this explicitly, receive it inside the function and use it.
function SumHits(_this) {
var inputs = $(_this).closest('.jq-roun.....
And in html,
<input class="jq-hitOrMiss" onchange="SumHits(this);".....
The problem arise to the this element which refers to window not the element which triggered the event. Thus you are getting the result
As you are using jQuery bind event using it like and get rid of ulgy inline-click handler.
$('.jq-hitOrMiss').on('change', SumHits)
This works for me, remove the on change on your html it is difficult to maintain
$('div.jq-roundContainer input.jq-hitOrMiss').change(function () {
var $parent = $(this).parents('.jq-roundContainer');
var sum = 0;
var inputs = $parent.find('.jq-hitOrMiss');
inputs.each(function () {
var value = $(this).val();
if (value == 'X' || value == 'x') {
sum++;
}
});
var totalInput = $parent.find('.jq-total');
totalInput.text(sum);
});
or if you want to keep your function
$('div.jq-roundContainer input.jq-hitOrMiss').change(SumHits);
function SumHits(){
var $parent = $(this).parents('.jq-roundContainer');
var sum = 0;
var inputs = $parent.find('.jq-hitOrMiss');
inputs.each(function () {
var value = $(this).val();
if (value == 'X' || value == 'x') {
sum++;
}
});
var totalInput = $parent.find('.jq-total');
totalInput.text(sum);
}

Categories

Resources