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);
}
Related
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>
I'm currently trying to create a live search for a specific column in a table. I've searched a bit but I can only find solutions to search over all columns. This is my code:
function searchInTable(table) {
var value = this.value.toLowerCase().trim();
jQuery(table).each(function (index) {
if (!index) return;
jQuery(this).find("td").each(function () {
var id = $(this).text().toLowerCase().trim();
var not_found = (id.indexOf(value) == -1);
$(this).closest('tr').toggle(!not_found);
return not_found;
});
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input id="search-table-input" class="search-table-input" type="text"
onkeyup="searchInTable('.table tr')" placeholder="Search Number...">
<table class="table">
<thead>
<tr>
<th class="table-number">
<span class="nobr">Number</span>
</th>
<th class="table-date">
<span class="nobr">Date</span>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<a>264</a>
</td>
<td>
<span>2019-01-02</span>
</td>
</tr>
<tr>
<td>
<a>967</a>
</td>
<td>
<span>2019-01-02</span>
</td>
</tr>
<tr>
<td>
<a>385</a>
</td>
<td>
<span>2019-01-02</span>
</td>
</tr>
<tr>
<td>
<a>642</a>
</td>
<td>
<span>2019-01-02</span>
</td>
</tr>
</tbody>
</table>
My function has some errors and don't work like it should.
How can I change my function that way that when I start typing only the number gets filtered? I need to make the function dynamically so that I can pass the column which should be used for the search.
The line that's causing it to search over all the columns is this one:
jQuery(this).find("td").each(function () {
...which takes each cell in the current row and looks to see if it contains value. If you only want to check as specific column, you should pass in the column index as something like columnIndex, and then you can select the correct column by doing jQuery(this).find("td").eq(columnIndex), using jQuery's .eq() function to select the correct one. The code should look something like this:
function searchInTableColumn(table, columnIndex) {
//check this.value exists to avoid errors
var value = this.value ? this.value.toLowerCase().trim() : "";
jQuery(table).each(function (index) {
if (!index) return;
var tableCell = jQuery(this).find("td").eq(columnIndex);
var id = tableCell.text().toLowerCase().trim();
var not_found = (id.indexOf(value) == -1);
$(this).closest('tr').toggle(!not_found);
});
}
Then you can call searchInTableColumn(table, 0) and it will only look in the first column.
I have a row click event. Recently had to add a checkbox to each row. How can I identify the clicked cell on row click event?
Or prevent row click when clicked on the checkbox.
Attempts: this.parentNode.cellIndex is undefined on the row click event.
function pop(row){
alert(row.cells[1].innerText);
}
<table style="width:100%">
<tr>
<th>Select</th>
<th>Site</th>
</tr>
<tr onclick="pop(this);">
<td><input type="checkbox" id="123456" /></td>
<td>Lonodn</td>
</tr>
</table>
Do you want something like this? You can just check the type attribute of the source element of the event and validate whether to allow it or not, you can stop the event using e.stopPropagation();return;.
function pop(e, row) {
console.log(e.srcElement.type);
if(e.srcElement.type === 'checkbox'){
e.stopPropagation();
return;
}else{
console.log(row);
alert(row.cells[1].innerText);
}
}
<table style="width:100%">
<tr>
<th>Select</th>
<th>Site</th>
</tr>
<tr onclick="pop(event, this);">
<td><input type="checkbox" id="123456" /></td>
<td>Lonodn</td>
</tr>
</table>
You should pass in the event details to your function and check the target property:
function pop(e){
// If the target is not a checkbox...
if(!e.target.matches("input[type='checkbox']")) {
alert(e.target.cellIndex);
}
}
<table style="width:100%">
<tr>
<th>Select</th>
<th>Site</th>
</tr>
<tr onclick="pop(event)">
<td><input type="checkbox" id="123456" /></td>
<td>Lonodn</td>
</tr>
</table>
Note: If you have nested elements inside the <td>, you might want to check e.target.closest("td") instead.
Note 2: You might need a polyfill for the matches method depending on which browsers you're supporting.
Here is an example if you don't want to attach a listener on every row :
document.getElementById("majorCities").addEventListener("click", function(e){
if(e.target.type === 'checkbox'){
var checked = e.target.checked;
var tr = e.target.parentElement.parentElement;
var city = tr.cells[1].innerHTML;
console.log(city+":checked="+checked);
}
});
<table id="majorCities" style="width:100%">
<tr>
<th>Select</th>
<th>Site</th>
</tr>
<tr>
<td><input type="checkbox"/></td>
<td>London</td>
</tr>
<tr>
<td><input type="checkbox"/></td>
<td>Paris</td>
</tr>
<tr>
<td><input type="checkbox"/></td>
<td>New-York</td>
</tr>
</table>
window.pop = function(row){
console.log('here');
var parent = row.parentNode;
Array.from(row.parentNode.querySelectorAll('tr')).forEach(function(tr, index){
if (tr === row) {
alert(index)
}
})
}
https://jsfiddle.net/sz42oyvm/
Here is for the pleasure, another example with an object containing the cities' names and a method to draw the table with ids corresponding to the name of the clicked city, so getting the clicked name is easier.
(function () {
var mySpace = window || {};
mySpace.cities = {};
mySpace.cities.pointer = document.getElementById("majorCities");
mySpace.cities.names = ["Select","City"];
mySpace.cities.data = [{"name":"Paris"},{"name":"New Delhi"},{"name":"Washington"},{"name":"Bangkok"},{"name":"Sydney"}];
mySpace.cities.draw = function(){
this.pointer.innerHTML="";
var html = "";
html+="<tr>"
for(var i=0;i < this.names.length;i++){
html+="<th>"+this.names[i];
html+="</th>"
}
html+="</tr>"
for(var i=0;i < this.data.length;i++){
html+="<tr>"
html+="<td><input id='"+this.data[i].name+"' type='checkbox'/></td>"
html+="<td>"+this.data[i].name+"</td>"
html+="</tr>"
}
this.pointer.innerHTML=html;
}
mySpace.cities.draw();
mySpace.cities.pointer.addEventListener("click", function(e){
if(e.target.type === 'checkbox'){
var checked = e.target.checked;
var city = e.target.id;
console.log(city+":checked="+checked);
}
});
})();
table {width:25%;background:#ccc;border:1px solid black;text-align:left;}
td,tr {background:white;}
th:first-of-type{width:20%;}
<table id="majorCities">
</table>
I have up to three almost-identical divs that contain tables of usernames. Some names may be repeated across the three divs. I'd like to highlight the names that are repeated. The first occurrence of the user should not be colored, the second occurrence should have an orange background and the third should have a red background. The divs go from left to right in order so the first div should not have any highlighted usernames.
My HTML looks like:
<div class="col-md-4">
<table class="table table-striped">
<tr>
<th>2/26/2014</th>
</tr>
<tr>
<td>user1</td>
</tr>
<tr>
<td>user2</td>
</tr>
<tr>
<td>user5</td>
</tr>
<tr>
<td>user17</td>
</tr>
</table>
</div>
<div class="col-md-4">
<table class="table table-striped">
<tr>
<th>2/27/2014</th>
</tr>
<tr>
<td>user13</td>
</tr>
<tr>
<td>user5</td>
</tr>
<tr>
<td>user7</td>
</tr>
</table>
</div>
<div class="col-md-4">
<table class="table table-striped">
<tr>
<th>2/28/2014</th>
</tr>
<tr>
<td>user5</td>
</tr>
<tr>
<td>user45</td>
</tr>
<tr>
<td>user1</td>
</tr>
</table>
</div>
I know that the username table cells will be selected with $('table.table td') (if I use jQuery) but I'm not sure what to do from there.
Please let me know if you have any questions.
Thank you.
Is this what you want?
I created a map to store text-occurrence pairs. Each time the text is repeated, the counter associated with it gets incremented. If the counter climbs to a certain value, the background will be set to another color. Give it a shot!
DEMO
var map = new Object();
$('td').each(function() {
var prop = $(this).text()
var bgColor = '#FFFFFF';
if (map[prop] > 1) {
bgColor = '#FF0000';
} else if (map[prop] > 0) {
bgColor = '#FF7F00';
}
$(this).css('background', bgColor);
if (map.hasOwnProperty(prop)) {
map[prop]++;
} else {
map[prop] = 1;
}
});
You could try something like this but I didn't test it
$('td').each(function(){
var text = this.html();
$('td:contains("'+text+'"):nth-child(2)').css({'background':'orange'});
$('td:contains("'+text+'"):nth-child(3)').css({'background':'red'});
});
Edit:
Not particularly elegant but it seems to work
http://jsfiddle.net/63L7L/1/
var second = [];
var third = [];
$('td').each(function(){
var text = $(this).html();
second.push($("td").filter(function() {
return $(this).text() === text;
})[1])
third.push($("td").filter(function() {
return $(this).text() === text;
})[2])
});
$(second).each(function(){
$(this).css({'background':'red'});
});
$(third).each(function(){
$(this).css({'background':'orange'});
});
With pure Javascript (ECMA5)
CSS
.orange {
background-color:orange;
}
.red {
background-color:red;
}
Javascript
Array.prototype.forEach.call(document.querySelectorAll('.table-striped td'), function (td) {
var textContent = td.textContent;
if (this.hasOwnProperty(textContent)) {
td.classList.add(++this[textContent] === 2 ? 'orange' : 'red');
} else {
this[textContent] = 1;
}
}, {});
On jsFiddle
How to modify the code to add row in the table dynamically by using javascript?
This is my existing code which is having other functionality. Need one button below the table to add row. I don't need a pop-up which will say how many rows do you want to add. Every single hit will add extra row.
Javascript
var editing = false;
function catchIt(e) {
if (editing) return;
if (!document.getElementById || !document.createElement) return;
if (!e) var obj = window.event.srcElement;
else var obj = e.target;
while (obj.nodeType != 1) {
obj = obj.parentNode;
}
if (obj.tagName == 'INPUT' || obj.tagName == 'A') return;
while (obj.nodeName != 'TD' && obj.nodeName != 'HTML') {
obj = obj.parentNode;
}
if (obj.nodeName == 'HTML') return;
var x = obj.innerHTML;
var y = document.createElement('input');
var z = obj.parentNode;
z.insertBefore(y, obj);
z.removeChild(obj);
y.value = x;
y.className = 'inp-edit';
y.onblur = saveEdit;
y.focus();
editing = true;
}
function saveEdit() {
var area = this;
var y = document.createElement('TD');
var z = area.parentNode;
y.innerHTML = area.value;
z.insertBefore(y, area);
z.removeChild(area);
editing = false;
}
document.onclick = catchIt;
HTML
<table border=1 class="display">
<thead>
<tr class="portlet-section-header results-header">
<th class="sorting">Operator ID</th>
<th class="sorting">Status</th>
<th class="sorting">First Name</th>
<th class="sorting">Last Name</th>
<th class="sorting">Email</th>
<th class="sorting">Role</th>
<th class="sorting_disabled">Select All
<br />
<input type="checkbox" onclick="checkAll(this);" name="check" />
</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Test1</td>
<td>Active</td>
<td>wsrc</td>
<td>wsrc</td>
<td>aa#aa.com</td>
<td>SE Admin</td>
<td>
<input type="checkbox" value="3" onclick="checkAllRev(this);" name="deleteItem" />
</td>
</tr>
<tr class="even">
<td>Test2</td>
<td>Inactive</td>
<td>EAI</td>
<td>SUBSYSTEM</td>
<td>aa#aa.com</td>
<td>API</td>
<td>
<input type="checkbox" value="4" onclick="checkAllRev(this);" name="deleteItem" />
</td>
</tr>
<tr class="odd">
<td>Test3</td>
<td>Inactive</td>
<td>Dunning</td>
<td>Portal</td>
<td>aa#aa.com</td>
<td>API</td>
<td>
<input type="checkbox" value="5" onclick="checkAllRev(this);" name="deleteItem" />
</td>
</tr>
</tbody>
</table>
You can make a template html for your row to be added, and append that html on click of the button.
For example, the row to be added contained two columns and looked some thing like this:
<tr>
<td>Test</td>
<td>Active</td>
</tr>
You can save this template in a variable. For example:
var template = "<tr><td>Test</td><td>Active</td></tr>";
Now append function of jQuery can be used to add row dynamically.
Since you need to add the row to tbody, following code can be used:
$("tbody").append(template);
A similar approach can be used to achieve your desired task.