jQuery copy and append a row with changes - javascript

I'm trying to copy a row, change some data, then append it to the end of a table:
$(document).ready(function(){
$("#show_new_rows_number").change(function(){
var table_body = $("tbody");
var master_row = table_body.clone().children('tr')[mr_selector];
master_row.children('td')[0].children('input').first().name = 'type['+(rows+1)+']';
master_row.children('td')[1].children('input').first().name = 'type['+(rows+1)+']';
});
});
Here's the HTML:
<html>
<head>
<title>Test</title>
</head>
<body>
<table>
<thead>
<tr>
<th>First Name:</th>
<th>Last Name</th>
</tr>
</thead>
<tbody>
<tr>
<td><input type="text" name="first_name[1]"></td>
<td><input type="text" name="last_name[1]"></td>
</tr>
</tbody>
</table>
<input type="number" min=1 max=10 value=1 id="show_new_rows_number">
</body>
</html>
I'm getting an error that master_row.children is not a function. I really stink at traversing and doing anything in Javascript when it comes to DOM manipulation. Please help! I haven't even gotten to appending the row to the table yet and already getting errors.

The problem is that the elements returned from the jQuery's .children() method, are not jQuery elements, they are HTML elements, thus they do not have any .children() method.
So, assuming that the rest of the code is correct (since this seems to be just a part of the javascript code), your code should be:
$(document).ready(function(){
$("#show_new_rows_number").change(function(){
var table_body = $("tbody");
var master_row = table_body.clone().children('tr')[mr_selector];
$(master_row.children('td')[0]).children('input').first().name = 'type['+(rows+1)+']';
$(master_row.children('td')[1]).children('input').first().name = 'type['+(rows+1)+']';
});
});
O such cases, use the developer tools to print some values on the console. If you run console.log($("tbody").clone().children('tr')[mr_selector].children('td')[0]); you will get just a HTML element, not an jQuery element.

Related

Which event to trigger, programmatically simulating user blur

I'm using Power Automate Desktop with an Execute Javascript flow to try to automate some user entry in a Quickbooks Online Payroll form.
When natively using the form, it seems there is an event triggered on blur to validate the numerical input, among other things.
Using the JS flow, updating the input values is not being recognized by the form as once I save it, it shows those inputs as empty.
So I thought I need to trigger the blur event to get the data to save. Here is my JS script:
function ExecuteScript() {
var $payrollTableRows = $("table").first().find("tbody > tr.enabled");
var $regHoursInput;
var decRegHours;
var $bonusInput;
var employeeName;
console.log('Power Automate: Rows Found: ' + $payrollTableRows.length);
$payrollTableRows.each(function(){
employeeName = $(this).find("td:eq(1)").find("a").text();
$regHoursInput = $(this).find("input[wageitemid='HOURLY_PAY']");
if($regHoursInput){
decRegHours = Number($regHoursInput .val());
$bonusInput = $(this).find("input[wageitemid='BONUS']");
$bonusInput.focus();
if($bonusInput){
$bonusInput.val(decRegHours);
$bonusInput.trigger('blur');
}
}
});
}
Here is the script that gets executed on focus and blur on the QB Payroll page.
Why does the script initiated triggers not fire this code?
UPDATE 1:
Adding image of page:
UPDATE 2:
Posting the PAD flow I used. Also got a good overview of this from this video. And how to use Loop and Loop Index from this article.
My Flow:
To do something similar without relying on the JavaScript you could use a variable and loops.
Html used for this flow:
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Column A</th>
<th>Column B</th>
<th>Column C</th>
<th>Column D</th>
</tr>
</thead>
<tbody>
<tr>
<td>A <input type="text" id="text-1-input" value="One"></td>
<td>B <input type="text" id="text-2-input" value="Two"></td>
<td>C <input type="text" id="text-3-input" value="Three"></td>
<td>D <input type="text" id="text-4-input" value="Four"onblur="txtOnblur();"></td>
</tr>
<tr>
<td><input type="text" id="text-5-input" ></td>
<td><input type="text" id="text-6-input" ></td>
<td><input type="text" id="text-7-input" ></td>
<td><input type="text" id="text-8-input" hidden></td>
</tr>
</tbody>
</table>
</div>
<script src="https://code.jquery.com/jquery-3.6.1.slim.min.js"></script>
<script>
function txtOnblur(){
$("#text-8-input").show(true);
$("#text-8-input").val('blur triggered!');
}
</script>
</body>
</html>
I used a bit of JavaScript to extract the number of columns and rows in the table, this could have been done with the flow function 'Extract data from web page', but I find the JavaScript a bit faster/easier.
function ExecuteScript() {
var table = document.querySelector('body > div > table');
var colCount = table.children[0].children[0].children.length;
var rowCount = table.children[1].children.length;
return `${colCount} ${rowCount}`
}
Declare one UI element of the first input box. You can reuse this element by replacing/changing the selector properties to use a variable.
In the flow assign the value that will match the HTML selector for the particular control.
and then use the same element wherever you want to change/extract a value (remember the variable now sets the UI element)
The full flow code (copy this and paste it to PAD to see the details)
There will be errors on your side, but you will see the flow.
WebAutomation.LaunchEdge.AttachToEdgeByUrl TabUrl: $'''http://localhost/stackoverAnswer/''' AttachTimeout: 5 BrowserInstance=> Browser
WebAutomation.ExecuteJavascript BrowserInstance: Browser Javascript: $'''function ExecuteScript() {
var table = document.querySelector(\'body > div > table\');
var colCount = table.children[0].children[0].children.length;
var rowCount = table.children[1].children.length;
return `${colCount} ${rowCount}`
}''' Result=> cols_rows
Text.SplitText.Split Text: cols_rows StandardDelimiter: Text.StandardDelimiter.Space DelimiterTimes: 1 Result=> ColsAndRows
Text.ToNumber Text: ColsAndRows[0] Number=> numCols
Text.ToNumber Text: ColsAndRows[1] Number=> numRows
LOOP colIdx FROM 1 TO numCols STEP 1
SET inputBoxVariable TO $'''text-%colIdx%-input'''
WebAutomation.GetDetailsOfElement BrowserInstance: Browser Control: appmask['Web Page \'http://localhost/stackoverAnswer/\'']['Input text \'text-1-input\''] AttributeName: $'''Own Text''' AttributeValue=> inputBoxValue
IF colIdx = 4 THEN
WebAutomation.Focus.Focus BrowserInstance: Browser Control: appmask['Web Page \'http://localhost/stackoverAnswer/\'']['Input text \'text-1-input\''] WaitForPageToLoadTimeout: 60
MouseAndKeyboard.SendKeys.FocusAndSendKeys TextToSend: $'''{Tab}''' DelayBetweenKeystrokes: 10 SendTextAsHardwareKeys: False
END
SET inputBoxVariable TO $'''text-%colIdx + 4%-input'''
IF inputBoxValue <> $'''Three''' THEN
WebAutomation.PopulateTextField.PopulateTextFieldUsePhysicalKeyboard BrowserInstance: Browser Control: appmask['Web Page \'http://localhost/stackoverAnswer/\'']['Input text \'text-1-input\''] Text: inputBoxValue Mode: WebAutomation.PopulateTextMode.Replace UnfocusAfterPopulate: False WaitForPageToLoadTimeout: 60
ELSE
WebAutomation.PopulateTextField.PopulateTextFieldUsePhysicalKeyboard BrowserInstance: Browser Control: appmask['Web Page \'http://localhost/stackoverAnswer/\'']['Input text \'text-1-input\''] Text: $'''Skip this one''' Mode: WebAutomation.PopulateTextMode.Replace UnfocusAfterPopulate: False WaitForPageToLoadTimeout: 60
END
END
How it runs:
I don't have QB but I put together a quick html page with a script.
This is the html
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>Column 1</th>
</tr>
</thead>
<tbody>
<tr>
<td>Value <input type="text" id="text-1-input" onblur="txtOnblur();">1</td>
</tr>
<tr>
<td>Value <input type="text" id="text-2-input" hidden></td>
</tr>
</tbody>
</table>
</div>
<script src="https://code.jquery.com/jquery-3.6.1.slim.min.js"></script>
<script>
function txtOnblur(){
$("#text-2-input").show(true);
$("#text-2-input").val('blur triggered!');
}
</script>
</body>
</html>
When I run the following script on the page with Power automate it triggers the onblur event on the textbox
function ExecuteScript() {
var $txt = $("input[id='text-1-input']");
$txt[0].onblur();
}
In action:
When I try and call the code in a similar way as you do I only get the list of controls linked to the blur event.
I'm assuming it's jQuery being used in QB. I tend to stick to native JavaScript when it comes to PAD, more typing, but less abstraction.

How To Grab Data In Any Table Row (HTML/JavaScript) Using Tree Traversal Methods

I have a table that looks like this -->
<table>
<tr style="display: none";><td class="index">index_value</td></tr>
<tr><td>section header</td></tr>
<tr>
<td class="name>Steven</td>
<td class="height">6 ft.</td>
<td><button class="add">Add</button></td>
</tr>
</table
and a js script that looks like this -->
<script>
$(".add").on('click', function(){
var the_name = $(this).closest("td").siblings(".name").text();
var the_type = $(this).closest("td").siblings(".type").text();
var the_index = $(this).parent().find("td.index").text();
}
</script>
As you can probably tell, I'm trying to get the values of certain td within this table. The first two variables work just fine because they are within the same table row; however, the last variable is not capturing the data I want (inside the index class).
I'm having some trouble understanding these methods of tree traversal and how I can grab data within a table row that is not the one which contains the button that is clicked. Any advice would be appreciated, thank you.
There were a few syntax errors in your code that you need to clean up, but the issue you're having with getting the value in the index cell is that you're not going far enough up the DOM tree to run the .find() command.
$(".add").on('click', function(){
var the_name = $(this).closest("td").siblings(".name").text();
var the_type = $(this).closest("td").siblings(".type").text();
var the_index = $(this).closest("table").find("td.index").text();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tr style="display: none";><td class="index">index_value</td></tr>
<tr><td>section header</td></tr>
<tr>
<td class="name">Steven</td>
<td class="height">6 ft.</td>
<td><button class="add">Add</button></td>
</tr>
</table>

How to access value from dynamic drop down list using Javascript/ jquery

I have created a dynamic drop down list. I want to access the value selected from the dynamically created drop down.
My HTML file
<!DOCTYPE html>
</head>
<body>
<table align="center" cellspacing="3" cellpadding="3">
<tr>
<td>County Name: </td>
<td><select id="county"><option value="">Select county</option></select></td>
</tr>
<tr>
<td>City: </td>
<td><select id="city"><option value="">Select city</option></select></td>
</tr>
</table>
<button class="btn" onclick="doThis()"">Go</button>
</body>
My js file:
function doc(id){return document.getElementById(id);}
function buildCounty(){
var opts=doc('county').options;
for(var i=0;i<arr.length;i++){
opts[opts.length]=new Option(arr[i][0],arr[i][0]);
}
doc('county').onchange=function(){
this.blur();
var val=this.value;
if(!val){return;}
var co=doc('city').options;
co.length=1;
for(var j=0;j<arr.length;j++){
if(arr[j][0]!==val){continue;}
else{
var temp=arr[j][1];
for(var k=0;k<temp.length;k++){
co[co.length]=new Option(temp[k],temp[k]);
}
break;
}
}
}
}
function doThis(){
}
window.onload=buildCounty;
</script>
On click of the button, doThis() function existing in js file is called. In that function, I want to access the value selected in the 2nd (City list) dropdown.
In jQuery you can do below:
I can see that you don't have any value for city option. Was that the reason you were not able to get a value? If yes, then you need to populate the values too in your markup.
function doThis() {
console.log($('#city').val());
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table align="center" cellspacing="3" cellpadding="3">
<tr>
<td>County Name: </td>
<td><select id="county"><option value="">Select county</option></select></td>
</tr>
<tr>
<td>City: </td>
<td><select id="city"><option value="SomeValue">Select city</option></select></td>
</tr>
</table>
<button class="btn" onclick="doThis()" ">Go</button>
It looks like you might be able to leverage more of jQuery for this list.
First, in Jquery there is no need for this line:
function doc(id) { document.getElementyById(id); }
You are able to use a CSS selector to get an element using jQuery:
$("#id-of-element");
You can even assign it to a variable:
var countyDropdown = $("#county");
Additionally, one of the "better" ways of running jQuery is to place the script tag at the end of your document and then wait for the document to raise the ready event:
$(document).ready(function () { .... });
Here is a quick codepen I put together that leverages jQuery to dynamically populate a dropdown from the selection in another dropdown. It also sets up the go button to do something when clicked.
codepen-dynamic-dropdown-selection

Clone full table and submit as form value

How to correctly copy/clone a html table with javascript and submit it to php, where it will be converted to PDF and XLS documents?
So far I have a html something like this:
<table class="planning-table">
<thead>
<tr>
<td>blah blah</td>
</tr>
</thead>
<tbody>
<tr>
<td>blah blah</td>
</tr>
</tbody>
</table>
<form id="exportPDFform" method="post">
Eksport
<input id="table_data" type="hidden" name="table" value="" />
</form>
and jquery:
$("#exportPDF").on("click", function (){
var value = $('.planning-table').clone();
$("#table_data").val( escape( value ) );
$("#exportPDFform").submit();
return false;
});
Now, the cloning works, but, when put inside input field, it gives me:
[object-Object]
How do I correctly do this?
PS: the form gets changed/created dinamically when selecting different filters, so it would be much easier to use the final product then to recreate it on PHP side.
Presumably escape() takes a string whereas you are providing a jQuery object. When you try and cast an object to a string javascript gives you "[object Object]". Instead, you need to retrieve the HTML value of the table, like this:
var value = $('.planning-table').clone().wrap('<div />').parent().html();
I do not think you want to use the Clone() function, instead I think you want to get the HTML content of an element, but using the .html() function on an element above the table.
<div id="table-container">
<table class="planning-table">
<thead>
<tr>
<td>blah blah</td>
</tr>
</thead>
<tbody>
<tr>
<td>blah blah</td>
</tr>
</tbody>
</table>
</div>
<form id="exportPDFform" method="post">
Eksport
<input id="table_data" type="hidden" name="table" value="" />
</form>
and
$("#exportPDF").on("click", function (){
var value = $('#table-container').html();
$("#table_data").val( escape( value ) );
$("#exportPDFform").submit();
return false;
});

Multiple elements with same Id in Javascript

I have a case where a html file contains multiple elements with the same ID name.
The table row contains 5 columns of which I need to consider 2,3,4,5 columns data.
<tr id='total_row'>
<td>Total</td>
<td>%(count)s</td>
<td>%(Pass)s</td>
<td>%(fail)s</td>
<td>%(error)s</td>
<td> </td>
</tr>
I have the above code at several places in the file. I need to add the respective values using javascript.
An ID is unique in an html page. You can call it THE ID as well wrt a page. You cannot have same ID for two different tags in a single page. But you can use class instead of and ID. Know about it here
So your HTML can be like
<tr class='total_row'>
<td>Total</td>
<td>%(count)s</td>
<td>%(Pass)s</td>
<td>%(fail)s</td>
<td>%(error)s</td>
<td> </td>
</tr>
As an example with jquery you can do something like this,
<!DOCTYPE html>
<html>
<head>
<style>
</style>
</head>
<body>
<table>
<tr class="one">
<td></td>
<td></td>
</tr>
<tr class="one">
<td></td>
<td></td>
</tr>
<tr class="one">
<td></td>
<td></td>
</tr>
</table>
<script src="jquery-1.11.0.min.js"></script>
<script>
$(document).ready(function() {
$(".one").eq(0).find('td').eq(0).html("I'm tracked");
// get 1st tr and get first td
$(".one").eq(1).find('td').eq(1).html("I'm tracked");
// get 2nd tr and get second td
$(".one").eq(2).find('td').eq(0).html("I'm tracked");
// get 3rd tr and get first td
});
</script>
</body>
</html>
But I guess this approach can be tedious.
Id should be unique and if you use the same id, javascript code refers only the first element. but if you still want to use same id than you may try the below code:
$(function(){
$('[id="total_row"]').each(function(){//run for every element having 'total_row' id
var $this = $(this);
$this.find('td').eq(1).text() //to get second column data
$this.find('td').eq(1).text('dummy text') //to set second column data
});
});
You can use XHTML:
<p id="foo" xml:id="bar">
Through XHTML you can apply similar ID to multiple Controls.
Similar questions can be found here:
http://www.c-sharpcorner.com/Forums/
While duplicate IDs are invalid, they are tolerated and can be worked around. They are really only an issue when using document.getElementById.
I'll guess that the table looks like:
<table id="t0">
<tr>
<td>-<th>count<th>Pass<td>Fail<td>Error<td>
<tr>
<td>-<td>1<td>1<td>0<td>0<td>
<tr>
<td>-<td>1<td>1<td>0<td>0<td>
<tr id='total_row'>
<td>Total<td><td><td><td><td>
<tr>
<td>-<td>1<td>1<td>0<td>0<td>
<tr>
<td>-<td>1<td><td>1<td>0<td>
<tr>
<td>-<td>1<td><td>0<td>1<td>
<tr id='total_row'>
<td>Total<td><td><td><td><td>
</table>
<button onclick="calcTotals();">Calc totals</button>
If that's correct, then a function to add each sub–section can be like:
function calcTotals(){
var table = document.getElementById('t0');
var rows = table.rows;
var row, totals = [0,0,0,0];
// For every row in the table (skipping the header row)
for (var i=1, iLen=rows.length; i<iLen; i++) {
row = rows[i];
// If it's a total row, write the totals and
// reset the totals array
if (row.id == 'total_row') {
for (var j=0, jLen=totals.length; j<jLen; j++) {
row.cells[j+1].innerHTML = totals[j];
totals[j] = 0;
}
// Otherwise, add values to the totals
} else {
for (var k=0, kLen=totals.length; k<kLen; k++) {
totals[k] += parseInt(row.cells[k + 1].innerHTML) || 0;
}
}
}
}
In addition to using classes, which works but feels kind of icky to me, one can also use data-* attributes.
<tr class='total_row' data-val-row-type="totals-row">
<td>Total</td>
<td>%(count)s</td>
<td>%(Pass)s</td>
<td>%(fail)s</td>
<td>%(error)s</td>
<td> </td>
</tr>
Then, in your script (jQuery syntax -- querySelectorAll has a similar syntax)
var $totalsRows = $("[data-val-row-type='totals-row']);
When you are in a team with a separate UI designer, this keeps the UI guy from ripping out and changing your class names to fix the new design layout and it makes it quite clear that you are using this value to identify the row, not just style it.

Categories