jQuery - Identify td that was clicked - javascript

I have created a grid (table) inside of an HTML doc based on user preference.
After appending the rows and cells to the table, it appears jQuery cannot access them properly? Its as if it doesn't consider them part of the DOM.
Essentially I will be handling everything inside of a multidimensional array for my data, such as:
[ ["","",""], ["","",""], ["","",""] ]
It is currently set up with appended rows named #row_0, #row_1, etc and td's added without an ID.
I need to be able to map the clicked TD to my jQuery so I can perform some functions and logic on it.
Is there any way to respond to my JS when a user clicks on a td with something like:
tr = [1]
td = [2]
Current code found here:
http://jsfiddle.net/HNjMR/4/

Here live example friend: http://jsfiddle.net/HNjMR/5/
$("#board").on('click', 'td', function(){
$("#board td").css("background","white");
$(this).css("background","red");
});
This happens because when you insert a new dynamic element on the page, ready jquery can not find it. The ready by default runs events only on existing elements at load time. While some functions like .on, solve this problem.

http://jsfiddle.net/898n2/1/
$( document ).on( "click", "td", function() {
alert(this.id);
});
This is using your current code
Notice I have used .on - this is a 'live' event using jquery
I have also modified your naming of each td to read col_1_2 - with 1 being col and 2 being row.
$("#row_"+i).append("<td id='col_"+j+"_"+i+"'> </td>");

when the td element click handler is added those target elements are not yet created so use event delegation and bind the click handler to the #board element with the target element selector as td as given below. Then use .index() to find the position of row and td(Add 1 to index as it is 0 based)
$("#board").on('click', 'td', function () {
var $td = $(this);
var td = $td.index() + 1,
row = $td.parent().index() + 1;
alert(td + '-' + row);
});
Demo: Fiddle
Also your create code is buggy, jQuery append does not work like string concatenation
$('#board tr').remove();
var size = parseInt($("#size").val());
for (var i = 0; i < size; i++) {
var $tr = $('<tr />', {
id: 'row_' + i
}).data('index', i + 1);
$("#board").append("<tr id='row_" + i + "'>");
for (var j = 0; j < size; j++) {
$('<td />', {
id: 'col_' + j
}).data('index', j + 1).appendTo($tr);
}
$("#board").append($tr);
}
Demo: Fiddle

Here the solution, remember that you should bind the eventclick when the elements was created, you souldn't use a generical $('td').click event, this does not work because some elements was not added in DOM yet.
$("#go").click(function(){
var size = $("#size").val();
if(size<3 || size>10){
alert("That is not a valid input. Please select 3-10");
return;
}
$('#board tr').remove();
// Check this bellow
var size = parseInt($("#size").val()),
tr = $("<tr/>"), td = $("<td/>"), tdc;
for(var i=0;i < size;i++){
for(var j=0;j<size;j++){
tdc = td.clone();
tdc.attr('id', 'col_'+j);
tr.append(tdc);
tdc.click(function(){
alert(this);
});
}
tr.attr('id','row_'+i);
$("#board").append(tr);
}
});

Related

Is it possible to add an eventlistener on an object that is modified through javascript?

I have a table where the sum of the rows are updated with javascript when a user changes the quantity within a popup modal. I want to be able to update the grand total of all my rows every time the total of a row change.
I have tried to add an event listener onchange and oninput on the total of each of my row but it doesn't work. I have tried to find a workaround by adding an event listener on the quantity the user is changing but to no effect. I have tried the code in the console and it is working but the event-listener doesn't seem to trigger.
document.getElementById("work_quantity_<%= #work_detail.id %>").addEventListener("change", grandTotal)
var work_detail_quantity = document.getElementById("work_quantity_<%= #work_detail.id %>").value;
var work_detail_block = document.getElementById("work-<%= #work_detail.id %>");
var quant = work_detail_block.getElementsByTagName('td')[1];
quant.innerHTML = work_detail_quantity+" <%= #work_detail.work_resource.unit_measure%>";
function grandTotal() {
var sub_total = document.getElementById("subtotal_lign").innerHTML
var table = document.getElementById("invoice_summary"), sumVal = 0;
for(var i = 1; i < table.rows.length; i++)
{
sumVal = sumVal+ parseFloat(table.rows[i].cells[4].innerText.replace(/[^0-9-.]/g, ''));
}
document.getElementById("subtotal_lign").innerHTML = sumVal.toFixed(2) + ' €' ;
};
If someone could give me a hint it would be more than appreciated, I am a rookie in javascript. Thank you
It's not a good idea to parse the DOM like that to get the values.
Solution 1
You get the object that contains the values of your table form Ruby. Something like that:
var works = <%= #works.to_json %>;
Then, when a user updates his order, you first update the object and then you update the table based on the updated object.
Solution 2
When a user update his order, you send the update to your server and you update the table in the server side.
Solution 3 (Not recommended)
If you still want to do it by parsing the DOM, have a look at MutationObserver to listen the changes in your table.
I confess, I haven't tried this per say; but I did try in jQuery a similar thing, mainly because your example lacks the detail of the table;
but you need to trigger the event when a change occurs, I assume here your not using standard input fields, so are you using like <td contenteditable></td>, they can't be monitored for change, but keyup and blur can be monitored, so if you want to trigger the calc on blur, when that event occurs, trigger your table sum event...
I'd actually use jQuery for this but here goes ...
document.getElementById("work_quantity_<%= #work_detail.id %>").addEventListener("change", grandTotal)
...
function grandTotal() {
var sub_total = document.getElementById("subtotal_lign").innerHTML
var table = document.getElementById("invoice_summary"), sumVal = 0;
for(var i = 1; i < table.rows.length; i++) {
sumVal = sumVal+ parseFloat(table.rows[i].cells[4].innerText.replace(/[^0-9-.]/g, ''));
}
document.getElementById("subtotal_lign").innerHTML = sumVal.toFixed(2) + ' €' ;
};
// something like this
(function() {
// i assume it's cell[4] that is being edited, and it's like contenteditable
var table = document.getElementById("invoice_summary");
for(var i = 1; i < table.rows.length; i++) {
/* assign listener on the cell,
when it blurs, trigger the update,
you could also monitor keyup
*/
table.rows[i].cells[4].addEventListener("blur", function(e) {
document.getElementById("work_quantity_<%= #work_detail.id %>").dispatchEvent("change", grandTotal);
});
}
})();
Updated
I thought that what I wrote below was working but actually the grand total is always one computation behind as it is doing the sum of the rows before the total of the modified row has been updated after a change of quantity.
Ok it is working with a workaround, but I still believe MutationObservers could be a good way to resolve this kind of problem in the future
Good tutorial: for MutationObservers
To make my event listener work I add to define it before the change in the quantity is made by the user (stupid isn't it)
1) Add the event listener to the object you are tracking
document.getElementById("work_quantity_<%= #work_detail.id %>").addEventListener("input", grandTotal)
2) This is where my object is changed with javascript
var work_detail_block = document.getElementById("work-<%= #work_detail.id %>");
var quant = work_detail_block.getElementsByTagName('td')[1];
quant.innerHTML = work_detail_quantity+" <%= #work_detail.work_resource.unit_measure%>";
3) I define the function which is called in the step 1
function grandTotal() {
var sub_total = document.getElementById("subtotal_lign").innerHTML
var table = document.getElementById("invoice_summary"), sumVal = 0;
for(var i = 1; i < table.rows.length; i++)
{
sumVal = sumVal+ parseFloat(table.rows[i].cells[4].innerText.replace(/[^0-9-.]/g, ''));
}
document.getElementById("subtotal_lign").innerHTML = sumVal.toFixed(2) + ' €' ;
};

Replacing entire DIV with function

I'm attempting to start a div area with html in it and then replace that html with new html. I am currently trying to do so with the use of a function to simplify the creation of the html.
This is my function that creates a table based on input of rows and columns and a character.
function drawArt (x, y, char){
$( "#artArea").append("<table>");
indexY = 0;
while (indexY < y)
{
$( "#artArea").append("<tr>");
var indexX = 0;
while (indexX < x)
{
$( "#artArea").append("<td class=tableCell>" + char + "</td>");
indexX++;
}
$( "#artArea").append("</tr>");
indexY++;
}
$( "#artArea").append("</table>")
};
I'd like to be able to recall this function to redraw the table. So far this is what I have written but it seems to not work. Any tips?
$( "#genNew" ).click(function(){
var xGlobal = $("#numCols").val();
var yGlobal = $("#numRows").val();
var charGlobal = $("#drawChar").val();
$( "#artArea" ).replaceWith();
drawArt (xGlobal, yGlobal, charGlobal);
})
So, as suggested below changing "replaceWith" to "empty" fixed part of my problem. However, it broke another part of my program. I should be able to click on any character and get it to change to whatever was input, without changing the whole table, as so:
$( ".tableCell" ).click(function(){
charGlobal = $("#drawChar").val();
$(this).text(charGlobal)
})
Which part of my program is failing?
The big problem is you're creating invalid html. You always append to the #artArea table, so your markup will end up as
<table>
<tr></tr>
<td></td>
<td></td>
... etc
</table>
This is not what you want. What I suggest you could do is to simply create the appropriate html as a string inside drawArt and use `replaceWith to change the html
function drawArt (x, y, char){
var html = "<table>";
indexY = 0;
while (indexY < y)
{
html += "<tr>";
//-- snip, you get the idea!
}
html += "</table>"
return html;
}
and then
$( "#genNew" ).click(function(){
var xGlobal = $("#numCols").val();
var yGlobal = $("#numRows").val();
var charGlobal = $("#drawChar").val();
$( "#artArea" ).html(drawArt (xGlobal, yGlobal, charGlobal));
})
Working example: http://jsfiddle.net/0sh1wt01/1/
Having updated the question with a new requirement, I should address that too. The reason your click event handler does not work on the table cells is that click only affects elements which are on the page at the time the page loads. if you're dynamically adding new elements (as we are above) then you need to delegate the event to an element which does exist at page load. In this case we could use the artArea. Note you want .html not .text
$( "#artArea" ).on('click','.tableCell', function(){
charGlobal = $("#drawChar").val();
$(this).html(charGlobal);
});
Live example: http://jsfiddle.net/0sh1wt01/2/
You can replace the html with a new one.
This is the js code:
$("#start").click(function() {
var x = $("#rows").val(),
y = $("#columns").val(),
charx = $("#char").val();
//getting teh no of columns, rows and the character
$("#container").html("");
//empty the container first
var table = document.createElement("table");
for (var i = 0; i < x; i++) {
var tr = document.createElement("tr");
for (var j = 0; j < y; j++) {
var td = document.createElement("td");
td.innerHTML = charx;
tr.appendChild(td);
}
table.appendChild(tr);
}
//creating the table based on the values
$("#container").append(table);
//appending inside the container
});
Here is the Plnkr Link
Hope it works for you :)

Creating a table of divs

I want to create a 16*16 table in html that holds div container in each cell. I'm not sure to what degree I'm allowed to mix jquery and pure javascript but what I have is
$( document ).ready(function() {
var table = Doucument.getElementById('table');
for (var i = 0; i <16; i++) {
var row = table.insertRow(i);
for(var j = 0; j < 16; j++){
row.insertCell(i);
}
};
});
This is adding a row to my table and then adding 16 cells. However I'm not sure how to add the div element to each cell. Perhaps theres a simpler way to do this with jquery? I'm not so proficient in jquery
Change "Document" to "document", remove the loop indexes (i, j) from the insertRow() and insertCell methods, and capture the newly inserted cell so that you can populate it. I've set each div's ID to be a combination of row and cell number in the example below.
I should also point out that there are better ways to do this. Tables should only be used for displaying data that requires tables. Also, this kind of thing would ideally be done on the server side unless there's a reason for you to do it in JavaScript.
That being said, here is a working example using JavaScript:
HTML:
<table id="myTable"></table>
JavaScript:
$(document).ready(function () {
var table = document.getElementById('myTable');
for (var i = 0; i < 16; i++) {
var row = table.insertRow();
for (var j = 0; j < 16; j++) {
var cell = row.insertCell();
var id = 'r' + i + 'c' + j;
cell.innerHTML = '<div id="' + id + '">' + id + '</div>';
}
};
});
CSS (after reading your comment about controlling size):
#myTable TD DIV {
height: 30px;
width: 30px;
}
Demo: http://jsfiddle.net/BenjaminRay/ugm613zg/
Do you have a specific reason to create a "table" - it is frowned upon by UX and CSS experts. It is considered a better approach to consider creating a table-like layout using Div/Spans and CSS. There are frameworks available that can provide you this layout style out of the box.
One of the most popular ones is Bootstrap's Grid - and here are some layout examples - http://getbootstrap.com/examples/grid/. The benefit of using this approach instead of tables is that your layout will adjust better to screen size changes like say viewing on a mobile device (called responsive layout).
In the interest of full disclosure Bootstrap supports 12 columns out of the box - but modifications are available for 16 and 24 columns - How to use bootstrap with 16 or 24 columns.
This is a longer route but a better solution than tables overall.
And using jQuery, you could do the following.
function addElems(ele, howMany, append) {
var $items = $();
for (var i = 0; i < howMany; i++) {
var $ele = $("<" + ele + "/>");
typeof append !== "undefined" && $ele.append(append);
$items = $items.add($ele);
}
return $items;
}
var $table = $("#myTable").append("<tbody></tbody>");
var $trs = addElems('tr', 16);
$table.append($trs);
$table.find("tbody > tr").each(function() {
var $tds = addElems('td', 16, "<div>My Div</div>");
$(this).append($tds);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id="myTable"></table>

Select the row TH of related TD

http://jsfiddle.net/wT3Ev/
How do i retrieve the text that is in the row TH of the selected td?
Yes i found some related posts, but nothing that does the trick for me.
I tried:
$(document).on("click", "#sTab td,#pvTab td", function () {
alert($('#sTab tr').find('th:nth-child(' + $(this).parent().index() + ')').innerHTML);
alert($(this).parent());
var th = $(this).closest('table').find('th').eq( this.cellIndex );
alert(th.innerHTML);
}
I'd suggest:
/* binds a click-handler to the 'tbody' element,
filters those clicks to only those that originate on a 'td' element:
*/
$('tbody').on('click', 'td', function(){
/* gets the siblings of the clicked 'td' element, filters those siblings
for 'th' elements, gets the first of those matching elements,
and then retrieves the text of that element, assigning it to the variable:
*/
var thText = $(this).siblings('th').first().text();
console.log(thText);
});
JS Fiddle demo.
Or, using a little more DOM (a tiny, tiny efficiency increase):
// same as above:
$('tbody').on('click', 'td', function(){
/* using DOM (not jQuery), moving from the clicked 'td' element
to its parentNode ('tr'), retrieving the first of its children
(JavaScript is zero-indexed, the first child is at position '0'),
and retrieving its textContent (the text of the 'th'):
*/
var thText = this.parentNode.children[0].textContent;
console.log(thText);
});
JS Fiddle demo.
References:
first().
on().
siblings().
text().
This should work:
var index = $(this).index();
console.log($(this).parents("table").find("th:eq(" + index + ")").text());
Edit: row header: console.log($(this).closest("tr").find("th").text());
Fiddle: http://jsfiddle.net/wT3Ev/4/
You weren't too far off. This works with your example:
$(document).on("click", "#sTab td,#pvTab td", function () {
var tdIndex = $(this).index(),
table = $(this).closest('table'),
headingIndex = tdIndex + 1,
thText = table.find('th:nth-child(' + headingIndex + ')').text();
alert(thText);
});
$(this).parent().find('th').html();
edit: explanation - row is always cell's parent so no need to look for index - just look for header in the row where you have the cell.
http://jsfiddle.net/qA6R9/

Hide table column ondblclick

I have a table and I want to hide a column when I double click a column.
Code for hiding a column is practically all around Stack Overflow. All I need is a hint on how/where to add the ondblclick event so I can retrieve the identity of a <td> within a <table>.
Here are two solutions that should work. One done with jQuery and one with only standard Javascript.
http://jsfiddle.net/GNFN2/2/
// Iterate over each table, row and cell, and bind a click handler
// to each one, keeping track of which column each table cell was in.
var tables = document.getElementsByTagName('table');
for (var i = 0; i < tables.length; i++) {
var rows = tables[i].getElementsByTagName('tr');
for (var j = 0; j < rows.length; j++) {
var cells = rows[j].getElementsByTagName('td');
for (var k = 0; k < cells.length; k++) {
// Bind our handler, capturing the list of rows and colum.
cells[k].ondblclick = column_hide_handler(rows, k);
}
}
}
// Get a click handler function, keeping track of all rows and
// the column that this function should hide.
function column_hide_handler(rows, col) {
return function(e) {
// When the handler is triggered, hide the given column
// in each of the rows that were found previously.
for (var i = 0; i < rows.length; i++) {
var cells = rows[i].getElementsByTagName('td');
if (cells[col]) {
cells[col].style.display = 'none';
}
}
}
}
With jQuery it is much cleaner. This method also uses event bubbling, so you don't need to bind an event handler to each table cell individually.
http://jsfiddle.net/YCKZv/4/
// Bind a general click handler to the table that will trigger
// for all table cells that are clicked on.
$('table').on('dblclick', 'td', function() {
// Find the row that was clicked.
var col = $(this).closest('tr').children('td').index(this);
if (col !== -1) {
// Go through each row of the table and hide the clicked column.
$(this).closest('table').find('tr').each(function() {
$(this).find('td').eq(col).hide();
});
}
});
You can do this way:
<td ondblclick="this.style.display = 'none';">Some Stuff</td>
Here this refers to current td clicked.
Working Example
To go unobtrusive, you can do that easily using jQuery if you want:
$('#tableID td').dblclick(function(){
$(this).hide();
});
Due to lack of answears I came up with a workaround, which is a big ugly, but it works fine.
On the window load event I decided to iterate the table and set each 's onclick event to call my show_hide_column function with the column parameter set from the iteration.
window.onload = function () {
var headers = document.getElementsByTagName('th');
for (index in headers) {
headers[index].onclick = function (e) {
show_hide_column(index, false)
}
}
}
show_hide_column is a function that can be easily googled and the code is here:
function show_hide_column(col_no, do_show) {
var stl;
if (do_show) stl = 'table-cell'
else stl = 'none';
var tbl = document.getElementById('table_id');
var rows = tbl.getElementsByTagName('tr');
var headers = tbl.getElementsByTagName('th');
headers[col_no].style.display=stl;
for (var row=1; row<rows.length; row++) {
var cels = rows[row].getElementsByTagName('td')
cels[col_no].style.display=stl;
}
}
Note: my html only had one table so the code also assumes this. If you have more table you should tinker with it a little. Also it assumes the table has table headers ();
Also I noted this to be an ugly approach as I was expecting to be able to extract the index of a table cell from the table without having to iterate it on load.

Categories

Resources