Using object reference (this) in called function - javascript

I have rewritten this question as parts of the original were solved to a simpler solution:
I have a dynamically created table where there will potentially be over 100 table cells () that wont have ID's for them. When a table cell is clicked a onclick event fires and a conditional check is done to determine if it is the first click of a 2 click series or the second click. The conditional determines which value of 2 hidden form fields is set.
Now here is the simple part im trying to accomplish: onclick, IF it is the first click I want the background color of the object that triggered the function to be color1 else if it is the second click then it will be color2.
The code: (JSFiddle Here)
CSS
#test tr td {text-align:center; border: thin black solid;}
SCRIPT
<script>
var x = 0;
var click = 0;
function res(zz) {
if (click == 0) {document.getElementById('start').value=zz; click = 1;} else
{document.getElementById('end').value=zz; click = 0;}
}
</script>
HTML
<form action="" method="POST">
<input type="hidden" name="start" id="start" value="">
<input type="hidden" name="end" id="end" value="">
<input name="submit" type="submit" value="submit" />
</form>
<div id="starget"></div>
<div id="etarget"></div>
<table width="100%" id="test">
<thead>
<tr>
<th>Tech</th><th>0800</th><th>0900</th><th>1000</th><th>1100</th><th>1200</th>
</tr>
</thead>
<tr>
<td>Bill</td>
<td>XXX</td>
<td onclick="res(0900);"></td>
<td>XXX</td>
<td>XXX</td>
<td onclick="res(1200);"></td>
</tr>
</table>
This change works IF i want the background color between the first click and second click to be the same:
<td onclick="res(0900);this.style.backgroundColor='green';"></td>
This below however does not work, since the calling object () passes no reference of itself (this.style....) to the function, however this is in fact the way i need it to work because i need the conditional check to determine what color to set the background to:
function res(zz) {
if (click == 0) {document.getElementById('start').value=zz; click = 1;this.style.backgroundColor='green';} else {document.getElementById('end').value=zz; click = 0;this.style.backgroundColor='red';} }

You simply need to pass 'this' to the function res
Fiddle here:
http://jsfiddle.net/sifriday/r3jaapj5/2/
HTML:
<td onclick="res(0900, this);"></td>
Corresponding JS tweak:
function res(zz, el) {
if (click == 0) {document.getElementById('starget').innerHTML=zz; click = 1; el.style.backgroundColor='green';} else {document.getElementById('etarget').innerHTML=zz; click = 0;}
}

Related

Show/Hide more than one <table> with a checkbox

I want to show and hide several tables on one page with one button. Unfortunately my script can only show and hide one table at a time.
I have a page with a lot of queries. There are also text fields in a table. For a better overview, the tables with the text fields should only be displayed when the checkbox is ticked. The checkbox should not be clicked at the beginning.
function Displayer(n)
{
var check = document.getElementById('Section'+n);
if (check.style.display == 'none')
{
check.style.display='inline';
}
else
{
check.style.display='none';
}
}
<p><input type="checkbox" class="btnstylega" onClick="Displayer(99)" />Show Tables</p>
<table id="Section99" style="display:none;"> <td>
AAAAAAAAAAAAAA
</td></table>
<table id="Section99" style="display:none;"> <td>
BBBBBBBBBBBBBBB
</td></table><br>
I want to show and hide many tables without adjusting the tables by clicking on the checkbox.
An ID must be unique in a document. The tool to mark multiple elements as part of a group is a class.
Replace your id attributes with class attributes.
Then replace getElementById with getElementsByClassName (or querySelectorAll).
These methods return lists of nodes and not single elements, so loop over the result like an array and access the style property on each one in turn.
The attribute id must be unique in a document, you can use class instead. You can use querySelectorAll() to target all the elements having the class, then loop through them to set the style. You can toggle the class using classList.toggle() like the following way:
function Displayer()
{
var check = document.querySelectorAll('.Section99');
check.forEach(function(table){
table.classList.toggle('show');
});
}
.Section99{
display: none;
}
.show{
display: block;
}
<p><input type="checkbox" class="btnstylega" onClick="Displayer()" />Show Tables</p>
<table class="Section99" class="hide"> <td>
AAAAAAAAAAAAAA
</td></table>
<table class="Section99" class="hide"> <td>
BBBBBBBBBBBBBBB
</td></table><br>
Improvement: It will add the event handler and trigger the change on load where needed
Note the data-attribute on the checkbox
var chg = new Event('change');
document.querySelectorAll(".btnstylega").forEach(function(but) {
but.addEventListener("change", function() {
var checked = this.checked,
section = this.getAttribute("data-tables");
document.querySelectorAll('.Section' + section).forEach(function(sect) {
sect.classList.toggle("hide",!checked);
});
})
but.dispatchEvent(chg);
});
.hide {
display: none;
}
<p><input type="checkbox" class="btnstylega" data-tables="88" checked />Show Tables 88 </p>
<p><input type="checkbox" class="btnstylega" data-tables="99" />Show Tables 99</p>
<table class="Section88">
<tr>
<td>
AAAAAAAAAAAAAA
</td>
</tr>
</table>
<table class="Section88">
<tr>
<td>
BBBBBBBBBBBBBBB
</td>
</tr>
</table><hr>
<table class="Section99">
<tr>
<td>
CCCCCCCCCCCCCCCC
</td>
</tr>
</table>
<table class="Section99">
<tr>
<td>
DDDDDDDDDDDDDDDDDDDD
</td>
</tr>
</table>

How to edit this code to make it use the current element?

I'm working on Symfony project, so I have a form that is generated by data-prototype. Basically is a form with 6 inputs, and I have a function that takes values of date.input and duration.input and must calculate a endingDate and paste it in a <h5>.
So, this form can be generated as many times as you want, and all inputs have same structure, like loan_charges_1_date, loan_charges_1_duration ... next one will be with same id, except the number... loan_charges_2_date... or any other number.
I implemented a script that is collecting all id's of date inputs, and all of duration, and make a array that contain this values in pair, like
pairs = [
[loan_charge_1_date, loan_charge_1_duration],
[loan_charge_2_date, loan_charge_2_duration],
[loan_charge_151_date, loan_charge_151_duration],
[loan_charge_302_date, loan_charge_302_duration],
];
From my requirements, each generated form is a table row with inputs in td.
First I introduce the date, then duration, and onkeyup of introducing duration, it must .html() (for now) value of date input, but from this form.
What I have now is that it works with .last.html(dateValue);, which makes me problem, cause if I generate at once more than 1 additional form, and start fill the first one, the hint h5 of output of LAST form is filled. Or if I fill forms one-by-one, but after that I want to edit a date or duration of previous form inputs, it will show a result not at current hint, but at the last one.
Please, watch my JSFiddle code with comments : https://jsfiddle.net/x2j72xou/3/ (but it is impossible to sent all the code, cause it's from many files and it's rendered from Symfony, and so on). A screenshot how it is now you can see here : http://imgur.com/IEORqHB
How it is possible to detect the index or id of each input was changed, and based on it, to fill that h5 hint, not the last one?
Thank you!
You are over-complicating the whole thing with those arrays and trying to use id selectors
Can do it something like:
// assign input event listener to date and duration <input>s
$('input[id$="_duration"], input[id$="_date"]').on('input', function(event){
// `this` is matching element that the event occurred on
// work within each row instance
let $row = $(this).closest('tr'),
// find() the inputs in this row
$durInput = $row.find('input[id$="_duration"]'),
startDate = $row.find('input[id$="_date"]').val(),
duration = $durInput.val();
// do some validation before proceeding
if(!startDate || ! duration){
return;
}
let endDate = calcEndDate(startDate, duration);
// add the <h5> if it isn't there yet
if(!$durInput.siblings('h5').length){
$durInput.after("<h5></h5>")
}
$durInput.siblings('h5').text(endDate)
});
function calcEndDate(startDate, duration){
// do whatever needed , returning start for now
return startDate
}
The important part of any repeating components such as this is to work within the main repeating container...in this case each <tr>
DEMO
I'm not exactly sure why you're making those pairs first, but it seems that you're only doing this so you know which date belongs to which duration. You don't really need to do that though, since you can easily find the matching elements inside the event handler. You know that the element that triggered the event is a duration input, so you just need to find the date input that is in the same row.
I've made an example below that does just that. I've stripped down your code to a bare minimum for running this snippet.
You also say that you can make a lot of these rows and I'm not sure if you mean to do this dynamically, so just to be safe I delegated the events.
$(function() {
$(document).on('keyup', '.loan_charges_duration', function() {
var $duration = $(this);// this is your duration input for this row
var $row = $duration.closest('tr');
var $date = $row.find('.loan_charges_date');// this is your date input for this row
// check for an h5 element after duration, else create and insert it
var $h5 = $duration.next('h5');
if ($h5.length==0) {
$h5 = $('<h5>').insertAfter($duration);
}
// set the html of the h5 element for this row
$h5.html('date: '+$date.val()+' | duration: '+$duration.val());
});
});
td {
vertical-align: top;
}
h5 {
border: 2px solid red;
padding: 4px;
margin: 4px 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<table>
<tr>
<td>1</td>
<td>
<label for="loan_charges_0_date">Date</label>
<input type="text" id="loan_charges_0_date" name="loan[charges][0][date]" class="form-control loan_charges_date" value="13/12/2001">
</td>
<td>
<label for="loan_charges_0_duration">Duration</label>
<input type="text" id="loan_charges_0_duration" name="loan[charges][0][duration]" class="form-control loan_charges_duration" value="11">
</td>
</tr>
<tr>
<td>2</td>
<td>
<label for="loan_charges_1_date">Date</label>
<input type="text" id="loan_charges_1_date" name="loan[charges][1][date]" class="form-control loan_charges_date" value="01/01/2001">
</td>
<td>
<label for="loan_charges_1_duration">Duration</label>
<input type="text" id="loan_charges_1_duration" name="loan[charges][1][duration]" class="form-control loan_charges_duration" value="24">
</td>
</tr>
<tr>
<td coslpan="3">...</td>
</tr>
<tr>
<td>151</td>
<td>
<label for="loan_charges_151_date">Date</label>
<input type="text" id="loan_charges_151_date" name="loan[charges][151][date]" class="form-control loan_charges_date">
</td>
<td>
<label for="loan_charges_151_duration">Duration</label>
<input type="text" id="loan_charges_151_duration" name="loan[charges][151][duration]" class="form-control loan_charges_duration">
</td>
</tr>
</table>

How to find next elements using jquery?

In my given example, i have two text boxes. when value in first text box changed i want to find the immediate next text box (note : without id) and change its value.
The example given contains only single text box group. actually it can be more than one text boxes. (group of from & to text boxes of Financial Data)
so, when value in from text box (txtFinancialYearFrom) changed, i want to find the to text box (txtFinancialYearTo) and change its value as well.
JsFiddle Link - Example
Thanks in advance for the help!!
<table class="fotm-table">
<tr>
<td class="text-right" width="120">
<span id="ContentPlaceHolder1_lblFinancialYear">Financial Data :</span>
</td>
<td>
<span>
<input type="text" id="txtFinancialYearFrom"
name="ctl00$ContentPlaceHolder1$txtFinancialYearFrom">
</span>
</td>
<td width="20" align="center">
<span style="align-content: center">to</span>
</td>
<td>
<span>
<input type="text" id="txtFinancialYearTo"
name="ctl00$ContentPlaceHolder1$txtFinancialYearTo">
</span>
</td>
</tr>
</table>
Using the given information, since you are going to have more blocks (that should be rows on your table), this solution should work:
var rows = $('.fotm-table tr');
$(rows).each(function(){
$('input:first', $(this)).on('change', function(){
var fromValue = $(this).val();
var row = $(this).closest('tr');
$('td:last input', row).val(parseInt(fromValue) + 1);
});
});
The code gets all the rows from your table and for each one of them, it will add a listener that when you change the first textbox (input), it will change the value of the next textbox (here it's adding 1 to it).
If I've understood correctly, you need something like this:
/* Loop through all table rows */
$('tr','table.fotm-table').each(function() {
var tr = this;
/* Cache all inputs a jquery object - you may want to specify which type of input you are targeting i.e. $('input[type="text"]') */
var inputs = $('input',tr);
/* Cache the slave (second in the example) input in a jquery object - you can do the same for multiple inputs, simply by modifying the eq() index parameter
*/
var slaveInput = inputs.eq(1);
/* Listen for changes on the master input */
var masterInput = inputs.eq(0).on('change',function() {
/* Do smt on the slave input - fill it with the next year in the example */
var year = $(this).val();
var followingYear = parseInt(year,10)+1
slaveInput.val(followingYear);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table class="fotm-table">
<tr>
<td class="text-right" width="120">
<span id="ContentPlaceHolder1_lblFinancialYear">Financial Year :</span>
</td>
<td>
<span>
<input type="text" id="txtFinancialYearFrom"
name="ctl00$ContentPlaceHolder1$txtFinancialYearFrom">
</span>
</td>
<td width="20" align="center">
<span style="align-content: center">to</span>
</td>
<td>
<span>
<input type="text" id="txtFinancialYearTo"
name="ctl00$ContentPlaceHolder1$txtFinancialYearTo">
</span>
</td>
</tr>
</table>
Here's an updated fork of the jsFiddle you provided:
https://jsfiddle.net/jkdaza/thonfzwu/5/
You can use this tricky solution from link:
$('#txtFinancialYearFrom').change(function(el) {
$(':input:eq(' + ($(':input').index(this) + 1) + ')').val('test');
});
https://jsfiddle.net/g34yqL0u/2/

Adding event handlers to each table row that will affect only that table row?

This isn't actually something I'm currently attempting to do; it just occurred to me while working with another table that I have no idea how I'd go about doing it, and the entire time on the train ride home I was puzzling over different solutions, none of which I could imagine working.
Imagine this situation: there is a table of 50 rows. On each row is a button. When clicked, this button should do something to the row it's on -- say, make all its text strikethrough, or make an AJAX call with the first cell's value, or something.
How would you go about binding those event handlers?
My initial thought was something like
buttons = document.getElementsByTagName("input");
rows = document.getElementsByTagName("tr");
for (i=0;i<buttons.length;i++) {
buttons[i].addEventListener('click',function() {
makeAjaxCall(rows[i]);
});
};
That is,
For each button in the document,
Add an event handler to that button,
Which will makeAjaxCall with the data of the row whose number corresponds to the button.
The problem, of course, being that makeAjaxCall will check the value of i when it's invoked, by which time, i is equal to buttons.length, and so the function will only ever work on the final row of the table.
So I suppose you'd need to find some way to actually hard-code the current value of i within the function handler... and that's something I don't even think is possible. Is it? How would you actually do something like this?
You can refer to the button object you are adding the event listener to using 'this'
Given the table in this format
<table>
<tr>
<td> <input type='button'> </td>
<td> 1st <td>
</tr>
<tr>
<td> <input type='button'> </td>
<td> 2nd <td>
</tr>
<tr>
<td> <input type='button'> </td>
<td> 3rd <td>
</tr>
</table>
The following code will allow you to access the data in the next cell, shown using console.log() rather than any ajax calls of course.
buttons = document.getElementsByTagName("input");
for (i=0;i<buttons.length;i++) {
buttons[i].addEventListener('click',function() {
makeAjaxCall(this);
});
};
function makeAjaxCall(btn) {
var sib=btn.parentNode.nextSibling;
while (sib.nodeName !='TD') {
sib=sib.nextSibling;
}
console.log(sib.innerHTML);
}
This can be extended to find any data in the row.
This section
while (sib.nodeName !='TD') {
sib=sib.nextSibling;
}
skips any extraneous characters (white space etc) between cells.
Fiddle Here
Interesting problem. I would go about doing it the way #kennebec suggests in the comment above.
Here is the fiddle: http://jsfiddle.net/u988D/
First, a slight change in markup to add data attributes
HTML
<table>
<tr>
<td data-to-json="id">1</td>
<td data-to-json="text">Lorem ipsum dolor.</td>
<td><button type="button">Click Me</button></td>
</tr>
<tr>
<td data-to-json="id">2</td>
<td data-to-json="text">Lorem ipsum dolor.</td>
<td><button type="button">Click Me</button></td>
</tr>
<tr>
<td data-to-json="id">3</td>
<td data-to-json="text">Lorem ipsum dolor.</td>
<td><button type="button">Click Me</button></td>
</tr>
</table>
Then the javascript. Probably can be optimized a bit more.
JS
var table = document.querySelector("table")
table.addEventListener("click", function(e) {
var element = e.target,
parent
//If the element is a button
if ( element && element.nodeName == "BUTTON" ) {
parent = element.parentNode
//Find the closest parent that is a TR
while ( parent.nodeName !== "TR" ) {
parent = parent.parentNode
}
//Convert Row to JSON
var json = {},
child
for ( var i = 0, _len = parent.children.length; i < _len; i++ ) {
child = parent.children[i]
if ( child.hasAttribute("data-to-json") ) json[child.getAttribute("data-to-json")] = child.innerText
}
// Do your AJAX stuff here
console.log(json)
}
})

onChange triggers only once in a while

i have the following problem:
I have a table with 2 input elements. If one of them bigger than the other, the whole row is supposed to be colored accordingly to the comparison. I trigger this with an onchange event and the first time while loading the site. The procedures work fine, but the onchange event only get triggered in 1 of 4 changes.
The table statement is the following:
<div class="grid">
<table id="table" class="tableRegion w100">
<thead style="text-align:left">
<tr>
<fmt:bundle basename="res/web/ec_resources">
<td class="tableHeader">
...
</td> ...
</fmt:bundle>
</tr>
</thead>
<tbody>
<c:set var="i" value="0"/>
<c:forEach var="participation" items="${someForm.list}">
<tr id="participationRow${i}">
And in here the code for the table > tr > td:
<td class="right bottom nowrap">
<div>
<c:out value="${someForm.event.currency.code}"/>
<fmt:formatNumber maxFractionDigits="2" minFractionDigits="2" var="ovFee" value="${overallFee}" />
<c:if test="${someForm.array[i].feeDebit != 0.0}">
<spring:input class="right debit" path="array[${i}].feeDebit" maxlength="7" size="6" onchange="isPaid(${i});" />
</c:if><c:if test="${someForm.array[i].feeDebit == 0.0}">
<spring:input class="right debit" path="array[${i}].feeDebit" maxlength="7" size="6" onchange="isPaid(${i});" value="${ovFee}"/>
</c:if>
</div>
</td>
<td class="right bottom nowrap">
<div class="padT5"><c:out value="${someForm.event.currency.code}"/> <spring:input class="right paid" path="array[${i}].feePaid" maxlength="7" size="6" onchange="isPaid(${i});"/></div>
</td>
The skript being called is the following:
$(document).ready(function(){
$('#table > tbody > tr').each(function(i) {
var paid = parseFloat($(this).find('input.paid').val());
var debit = parseFloat($(this).find('input.debit').val());
if (paid == debit)
$('#participationRow'+i).addClass("green");
else if (paid > debit)
$('#participationRow'+i).addClass("yellow");
}
);
});
function isPaid(i){
var paid = parseFloat($('#participationRow'+i).find('input.paid').val());
var debit = parseFloat($('#participationRow'+i).find('input.debit').val());
if (paid == debit)
$('#participationRow'+i).addClass("green");
else if (paid > debit)
$('#participationRow'+i).addClass("yellow");
}
What is the reason for the event being triggered just once in a while? I cross-checked with jQuery and onclick. They all get only triggered once in a while. No matter if I leave by clicking away or tabbing away.
Your code lacks any definition of a trigger when this is supposed to run.
The first part (the .each() block) is only executed on $(document).ready();, which means it is executed once per page load (right after the document has finished loading.)
The isPaid() function is nothing more than a function, I don't see where it is being called as it is not present in the current code snippet.
Your code seems OK by itself, but it just lacks any form of trigger.
You would expect something like:
$("input").change(function() {
var the_row = $(this).parent().parent(); //finds the <tr> it belongs to.
var i = the_row.attr("id").replace("participationRow","");
isPaid(i);
});
There are ways to improve on this, but this is the vital key you're missing in your code.
function highlightIfPaid(i){
var paid = parseFloat($('#participationRow'+i).find('input.paid').val());
var debit = parseFloat($('#participationRow'+i).find('input.debit').val());
if (paid == debit)
$('#participationRow'+i).addClass("green");
else if (paid > debit)
$('#participationRow'+i).addClass("yellow");
}
$(document).ready(function(){
//just use jquery for binding to change event,
//my guess is that Spring doesn't have a value for i at the point and jquery is not loaded by then so $ is undefined
$('input.debit').change(function(){
//you need to get the index i based on your table structure
//I assumed this is just the index of the row
//I get this method from this other post http://stackoverflow.com/questions/1620391/find-table-row-index-using-jquery
var i = $(this).closest('tr').prevAll().length;
highlightIfPaid(i);
});
//Dry: highlight your rows on first load
$('#table > tbody > tr').each(highlightIfPaid);
});
Okay... looking a while and with the help of #kabaros und #Flater I found the following which worked:
$(document).ready(function(){
$('input.paid').change(function(){
var i = $(this).closest('tr')[0].sectionRowIndex;
isPaid(i);
});
The solution was the $('input.paid') which got triggered, rather than $('input'). And counting the row was not that easy as well, but the above code counts the rows right. Unfortunately the onchange in the input-field only triggers once in a while!

Categories

Resources