JS onclick event in table Collapsibles/Accordion - javascript

I am trying to find out how to create onClick event in a table, that works according to the row clicked, displaying hidden siblings.
This table comes from a DB (ajax call)
$("#table-data").append(
`<tr id="${response.data[i]["id"]}">
<td id="nameTag${response.data[i]["id"]}"> ${response.data[i]["firstName"] + " " + response.data[i]["lastName"]}</td>
<td> ${response.data[i]["id"]} </td>
<td> ${response.data[i]["jobTitle"]} </td>
<td> ${response.data[i]["email"]} </td>
<td> ${response.data[i]["department"]} </td>
<td> ${response.data[i]["location"]} </td>
</tr>`
Here is where I want to get the row on click, but the id should be nameTag[i], where i is the row id.
const nameTag = $("#nameTag1"); //example: if click on row 1 should be this ID.
const nameTag = $(`#nameTag${response.data[i]["id"]}`); //I tried this way using it inside the ajax call. but it makes open and close row 12 to the infinite...
$(document).on("click", nameTag, function () {
$('#nameTag1').siblings().toggle("slow");
});
I tried this for loop but also doesn't work
const table = document.getElementById('table');
for (var i = 0, row; row = table.rows[i]; i++) {
//do something....
}
If I use fixed ID values for test, like onClick just for nameTag1, opening his siblings, when i click in any row open row 1...
I don't know, but probably I am going to the wrong way, maybe I should use just CSS...

Related

Get cell Value of Table html

I have this code (opt1) to get the text of the cell, which is clicked, but this alerts all of the objects in the table as its in a loop. But when I place it outside the loop I just get a random text from the table and not the one which is clicked (opt2).
I need the textContent of the cell, which is clicked, once, and not any other.
FYI: the cell contains multiple <td> inside <tr>.
The current method gets me the name text either of all cells (opt1) or the last cell (opt2)
HTML:
<table id="table">
<tr class="cell" onclick="RowHandlers()">
<td class="name">Name</td>
<td class="date">Date</td>
<td class="subject">Subject</td>
</tr>
</table>
opt1
function RowHandlers() {
var table = document.getElementById("table");
var name;
for (var i=1;i<table.rows.length;i++) {
name = table.rows[i].cells[0].innerText;
alert(name);
}
}
opt2
function RowHandlers() {
var table = document.getElementById("table");
var name;
for (var i=1;i<table.rows.length;i++) {
name = table.rows[i].cells[0].innerText;
}
alert(name);
}
Your problem seems to be identifying the clicked row after a click has happened.
We can solve this in multiple ways:
In JS, use the event object's target property:
(Preferred) Add handler via addEventListener().
Use a single handler by making use of event propagation.
Add handlers to each row.
(Discouraged) Add handler via onevent property. Only one handler can be added this way.
(Deprecated) Use the global object window.event.
(Discouraged) In HTML, use inline event onclick on each row:
Pass this to the handler function (e.g. <tr onclick="RowHandler(this)">). this in inline events will refer to the clicked element.
Add IDs to each row (e.g. <tr id="row1">). For each row, pass its ID to the function (e.g. onclick="RowHandler('row1')"). Search for the element by the passed ID.
Using addEventListener()
To add a listener, pass the event type and the listener:
const input = document.querySelector("input");
const button = document.querySelector("button");
button.addEventListener("click", clickHandler);
function clickHandler(event) {
console.log(input.value);
input.value = "";
}
<input><button>Click me!</button>
Event propagation
Because events bubble up the DOM, <table>'s listener will also fire when its rows are clicked. We can use event.target to get the event's origin element and find the clicked row:
const table = document.querySelector("table");
table.addEventListener("click", tableClickHandler);
function tableClickHandler(event) {
const row = event.target.closest("tr.cell"); // event.target may be a <td>
const isInRow = Boolean(row);
const isFirstRow = row === table.rows[0];
if (!isInRow || isFirstRow) return;
const name = row.cells[0].textContent;
const date = row.cells[1].textContent;
const subject = row.cells[2].textContent;
console.log("Clicked:", { name, date, subject });
}
table,td{border:1px solid}
<table>
<tr>
<th>Name</th>
<th>Date</th>
<th>Subject</th>
</tr>
<tr class="cell">
<td class="name">Some name</td>
<td class="date">01-01-1999</td>
<td class="subject">Some subject</td>
</tr>
<tr class="cell">
<td class="name">Another name</td>
<td class="date">02-02-2020</td>
<td class="subject">Another subject</td>
</tr>
</table>
Regarding your solutions
You are missing some way of identifying the clicked element; see my suggestions above.
In opt2, you are looping through all rows, but constantly overriding the variable name before using it. Therefore it is the same as only reading the name of the last row.
You may want to use .textContent over .innerText, because it doesn't cause a reflow. If visibility of the content matters, choose .innerText, otherwise .textContent.
A table usually consists of sections (e.g. <thead>, <tbody>, <tfoot>, or an implicit section). Each section consists of rows; each row consists of cells. Therefore, your class name "cell" for a row may be confused with actual table cells (<td>/<th>).
Please check this.
const [row] = document.getElementsByClassName('cell');
row.addEventListener('click', function(event) {
console.log(event.target.innerText);
})
<table id="table">
<tr class="cell">
<td class="name">Name</td>
<td class="date">Date</td>
<td class="subject">Subject</td>
</tr>
</table>

Delete table row button removes all subsequent rows

I have a table I've built in an app that when I click delete removes all rows following the row that was deleted on submit. That means that the table looks good to the user with just that one row removed, but when it hits the viewmodel on post action, those subsequent rows aren't included.
I've added some pretty complex code that goes all over the place to edit the index values of the rows but that ended up confusing the problem even more with some values replacing others and some other values just being set to 0. I know there has to be a more simple way.
I set this example back to the more simplified version where the delete appears to work well but then doesn't include any subsequent values in the viewModel when it hits the controller.
Here is the HTML Table
<input type="button" value="Add" class="button is-primary" id="btnAdd" />
<table id="tblPart" style="table-layout:fixed; width:100%;">
<thead>
<tr>
<th>
Part Number
</th>
<th>
Quantity
</th>
<th></th>
</tr>
</thead>
<tbody>
#for (var i = 0; i < Model.NumberModel.Count(); i++)
{
<tr >
<td >
#Html.TextBoxFor(modelItem => Model.NumberModel[i].PartNum, new { id = $"part{i}", required = "required", #class = "partTest" })
</td>
<td>
#Html.TextBoxFor(modelItem => Model.NumberModel[i].Quantity, new { type = "number", min = "0", id = $"quantity{i}", required = "required" })
</td>
<td>
<input type='button'
onclick='Remove(this)'
value='Remove' />
</td>
</tr>
}
</tbody>
</table>
Here is the JS
<script>
function Remove(button) {
//Determine the reference of the Row using the Button.
var row = $(button).closest("TR");
var name = $("TD", row).eq(0).html();
//console.log(row + name);
var index = 0;
var table = $("#tblPart")[0];
//Delete the Table row using it's Index.
table.deleteRow(row[0].rowIndex);
}
</script>
Thank you for your assistance.
When you delete the row, all subsequent rows index is wrong, you need to re-index the remaining rows on delete. If you for instance delete row with index 3 and then you have rows 0-2 and rows 4-6, 4-6 will be left out since there is no index 3, to fix this, you need to reindex the id and name attributes on the form fields after delete, also, you should consider using const and let in your function as var should be used for global variables, lastly, I added jquery tag to your post as you are mixing javascript and jquery in your code:
function Remove(button) {
//Determine the reference of the Row using the Button.
const row = $(button).closest("TR");
const name = $("TD", row).eq(0).html(); //this seems unnecessary
let index = 0; //this seems unnecessary
const table = $("#tblPart")[0];
//Delete the Table row using it's Index.
table.deleteRow(row[0].rowIndex);
//re-index
$('#tblPart tbody tr').each(function(i, el) {
//get the form fields
const $partnuminput = $(el).find('input[name*="PartNum"]');
const $quantityinput = $(el).find('input[name*="Quantity"]');
//use the iterator parameter of .each to set the index correctly
$partnuminput.attr("name", $partnuminput.attr("name).replace(/\d+/g, i);
$partnuminput.attr("id", $partnuminput.attr("id").replace(/\d+/g, i);
$quantityinput.attr("name", $partnuminput.attr("name).replace(/\d+/g, i);
$quantityinput.attr("id", $partnuminput.attr("id").replace(/\d+/g, i);
});
}

Get ID of <td> to change inner HTML multiple times

So I problem is that I want to generate a random ID for each in each row and then change the inner HTML of the specified whenever a certain button is clicked.
I have tried adding the onload attribute for each row that is created to try to get the id with a function and then pass the id when the function is called but it didn't work
function add(){
let taskInfo = `
<tr>
<td>${generateId(4)}</td>
<td>${productID}</td>
<td>-</td>
<td class="taskid"id="${generateId(4)}">Idle</td>
<td>
<span class="action-start" id="${generateId(4)}"onclick="start(getId())">Start</span>
<span class="action-stop" >Stop</span>
</td>
</tr>
`
let newTask = tbody.insertRow(tbody.rows.length);
newTask.innerHTML = taskInfo;
console.log("New task added!")
}
This is the function that adds the rows to the table ^
So what I am expecting to get is that when the users click start the status changes to a certain status, individually in each row

Adding custom attribute values of dynamically created dropdowns to another element

I have a bit of HTML here:
<tr taskId="(#=obj.task.id#)" assigId="(#=obj.assig.id#)" class="assigEditRow" >
<td><select name="resourceId" class="get-resources formElements"></select></td>
<td><span class="resources-units"></span></td>
<td><span class="resources-quantity"></span></td>
<td><input type="text" placeholder="Required Q"></td>
<td align="center"><span class="teamworkIcon delAssig" style="cursor: pointer">d</span></td>
</tr>
And a bit of JS here:
'use strict';
function addResourceFunction(){
let ResourcesJSON = (json) => {
let Resources = json;
console.log(Resources);
let contactsLength = json.length;
let arrayCounter = -1;
let resID;
let resName;
let resUnit;
let resQuantity;
let Option = $('<option />');
let assignedID = $('tr.assigEditRow:last').attr("assigId");
while(arrayCounter <= contactsLength) {
arrayCounter++;
resID = Resources[arrayCounter].ID;
resName = Resources[arrayCounter].name;
resUnit = Resources[arrayCounter].unit;
resQuantity = Resources[arrayCounter].quantity;
$('.assigEditRow').last().find('select').append($('<option>', {
value: resName.toString(),
text: resName.toString(),
resourceID: resID.toString(),
resourceUnit: resUnit.toString(),
resourceQuantity: resQuantity.toString()
}));
}
}
$.getJSON("MY JSON URL IS HERE", function(json) {
ResourcesJSON(json);
});
};
So what's actually going on here: I get my data from the URL (JSON array), trigger the addResourceFunction() on click to create a new table row and to add a new select with options passed from the array. As you see from my HTML markup, the select input is placed in td.get-resources, and all that works good. I get my date set, I populate the select field and all works good. I can add as many rows/select dropdowns as I want.
Also, every option has a few custom attributes (you can see it in my JS code above), and I want to add the values of those attributes to the second and third column of the row (in HTML those are span.resources-units and span.resources-quantity). The thing is, I have no clue how to make it work 1:1, meaning that one select dropdown "alters" only units and quantity of its own row. Below is the code for that:
let idCounter = 1;
$(document).on('change', '.get-resources', function() {
$('.assigEditRow').last().find('.resources-units').attr('id', 'units-' + idCounter);
$('.assigEditRow').last().find('.resources-quantity').attr('id', 'quantity-' + idCounter);
this.resourceUn = $( ".get-resources option:selected" ).attr( "resourceUnit" );
this.resourceQuant = $( ".get-resources option:selected" ).attr( "resourceQuantity" );
$('#units-' + idCounter).append(this.resourceUn);
$('#quantity-' + idCounter).append(this.resourceQuant);
idCounter++;
});
What happens is that if I add one select input, and change options, the thing works. When I add another one and change its options, it gets attributes of the first one. Adding more - same thing. Whatever I change, it takes the attribute value of the first item added.
Try getting the id from the element instead of from the variable, since you always update the element with the id of the counter, instead of the element with the id of the row that was clicked.
Hmm, what does the counter do exactly? The more I look at it, the less I understand. What I do know is that you're not selecting the correct elements by using the idCounter to reference the correct row.
You want to do something like
$(document).on('change', '.get-resources', function() {
//var row = this;
this.find(/* Some path to the second column */).att(/* some att to change */);
this.find(/* Some path to the third column */).att(/* some att to change */);
});
where you always use the row as the root again, instead of finding a certain id, so you only update that row.
Native:
<table>
<tr>
<td>
<select>
<option data-text="resName1" data-resourceID="resID1" data-resourceUnit="resUnit1" data-resourceQuantity="resQuantity1">1</option>
<option data-text="resName2" data-resourceID="resID2" data-resourceUnit="resUnit2" data-resourceQuantity="resQuantity2">2</option>
<option data-text="resName3" data-resourceID="resID3" data-resourceUnit="resUnit3" data-resourceQuantity="resQuantity3">3</option>
</select>
</td>
<td>
<div class="column2"></div>
</td>
<td>
<div class="column3"></div>
</td>
</tr>
</table>
<script>
document.addEventListener('change', function ( event ) {
var select = event.target,
option = select.options[select.selectedIndex],
values = {
'text' : option.getAttribute('data-text'),
'resourceID' : option.getAttribute('data-resourceID'),
'resourceUnit' : option.getAttribute('data-resourceUnit'),
'resourceQuantity' : option.getAttribute('data-resourceQuantity')
},
row = select.parentNode.parentNode,/* depending on how deep the select is nested into the tr element */
column2 = row.querySelector('.column2'),
column3 = row.querySelector('.column3');
column2.textContent = 'some string with the values you want';
column3.textContent = 'some string with the other values you want';
});
</script>
Basically you start with the select that was changed, from there you get the option node that was clicked. Then you get the attributes you need from that option. Then you go up a few nodes to the row parent and find the two columns inside that row. Then you can set the content of these two columns.

pass var to function() js with jquery

Just looking for the best practise way of doing this.
I have a table listing information and in the last column is a button with "Edit/View". When the user clicks on the button a div area appears with more information which can be edited
Code below with some fragments of jstl
<script type="text/javascript">
//Click on Edit/View on table
$('.viewCustomer').click(function()
{
.......
});
</script>
<tr class="odd">
<td>${customerBean.comName}</td>
<td>${customerBean.comCode}</td>
<td class="noRightPad"> <input type="submit" name="createBut" value="View/Edit" class="viewCustomer" /> </td>
</tr>
So my question would be:
(1) how do i pass a variable to function $('.viewCustomer').click(function()
(2) is this the best way of going about to do this. Is there a more efficient/secure/cleaner of doing this?
Cheers
Alexis
The click function will not be called by you. It is called when the button is clicked, and as such has the event object passed to it:
$('.viewCustomer').click(function(evt){
.......
});
What exactly are you wanting to pass? You can access the DOM element that you are clicking using this and $(this), so maybe it possible to reference what you want from here.
EDIT For comment
if the user clicked on the button that
was in the 4th row of the table and in
that row the another colum had
customer id 1234 i want to pass the
variable 1234.
NOTE: None of the below has been tested, but ought to suffice
Let's assume your 'customer id' column has a classname of 'customerid'. So your HTML might be:
<tr class="odd">
<td>${customerBean.comName}</td>
<td class="customerid">${customerBean.comCode}</td>
<td class="noRightPad"> <input type="submit" name="createBut" value="View/Edit" class="viewCustomer" /> </td>
</tr>
The jQuery might look something like:
$('.viewCustomer').click(function(){
var $buttonCell = $(this).parent(); //the <td> containing the button
var $buttonRow = $buttonCell.parent(); //the <tr> containing the button and your customer id
var $customerIdCell = $buttonRow.find("td.customerid");
var customerId = $customerIdCell.text();
});
The above is proken down into lines to show you how stuff is being retrieved. Using 'chaining' we can express it more concisely:
$('.viewCustomer').click(function(){
var customerId = $(this).parent().parent().find("td.customerid").text();
}
You can also search for the customerid cell as a 'sibling' of the button cell for an even more concise approach (and one less function call).
$('.viewCustomer').click(function(){
var customerId = $(this).parent().siblings("td.customerid").text();
}

Categories