I have a table like the following:
<table>
<tr>
<td>1</td><td>1</td><td>1</td>
</tr>
<tr>
<td>2</td><td>2</td><td>2</td>
</tr>
<tr>
<td>3</td><td>3</td><td>3</td>
</tr>
</table>
When a user clicks on the table, how can I get the index of this row (tr element)?
For example, when I click on the first tr (with 1s in the above table), it should pick it up and return 1.
This would get you the index of the clicked row, starting with one:
$('#thetable').find('tr').click( function(){
alert('You clicked row '+ ($(this).index()+1) );
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id="thetable">
<tr>
<td>1</td><td>1</td><td>1</td>
</tr>
<tr>
<td>2</td><td>2</td><td>2</td>
</tr>
<tr>
<td>3</td><td>3</td><td>3</td>
</tr>
</table>
If you want to return the number stored in that first cell of each row:
$('#thetable').find('tr').click( function(){
var row = $(this).find('td:first').text();
alert('You clicked ' + row);
});
A better approach would be to delegate the event, which means catching it as it bubbles to the parent node.
delegation - overview
This solution is both more robust and efficient.
It allows the event to be handled even if more rows are dynamically added to the table later, and also results in attaching a single event handler to the parent node (table element), instead of one for each child node (tr element).
Assuming that the OP's example is a simplified one, the table's structure can be more complex, for example:
<table id="indexedTable">
...
<tr>
<td><p>1</p></td>
<td>2</td>
<td><p>3</p></td>
</tr>
</table>
Therefore, a simplistic approach such as getting e.target.parentElement will not work, as clicking the internal <p> and clicking the center <td> will produce different results.
Using delegation normalizes the event handling, only assuming that there are no nested tables.
implementation
Both of the following snippets are equivalent:
$("#indexedTable").delegate("tr", "click", function(e) {
console.log($(e.currentTarget).index() + 1);
});
$("#indexedTable").on("click", "tr", function(e) {
console.log($(e.currentTarget).index() + 1);
});
They attach a listener to table element and handle any event that bubbles from the table rows. The current API is the on method and the delegate method is legacy API (and actually calls on behind the scenes).
Note that the order of parameters to both functions is different.
example
A comparison between direct handler attachment and delegation is available below or on jsFiddle:
$("#table-delegate").on("click", "tr", function(e) {
var idx = $(e.currentTarget).index() + 1;
$("#delegation-idx").text(idx);
console.log('delegated', idx);
});
$("#table-direct tr").on("click", function(e) {
var idx = $(e.currentTarget).index() + 1;
$("#direct-idx").text(idx);
console.log('direct', idx);
});
$('[data-action=add-row]').click(function(e) {
var id = e.target.dataset.table;
$('#' + id + ' tbody')
.append($('<tr><td>extra</td><td>extra</td><td>extra</td></tr>')[0])
});
tr:hover{
background:#ddd;
}
button.add-row {
margin-bottom: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet"/>
<h1>Event handling test</h1>
<p>Add rows to both tables and see the difference in handling.</p>
<p>Event delegation attaches a single event listener and events related to newly added children are caught.</p>
<p>Direct event handling attaches an event handler to each child, where children added after the inital handler attachment don't have a handler attached to them, and therefore their indices won't be logged to console.</p>
<h2>Delegation</h2>
<p><span>row index: </span><span id="delegation-idx">unknown</span></p>
<button class="add-row" data-action="add-row" data-table="table-delegate">Add row to delegation</button>
<table id="table-delegate" class="table">
<tbody>
<tr>
<td>normal</td>
<td>normal</td>
<td>normal</td>
</tr>
<tr>
<td><p>nested</p></td>
<td><p>nested</p></td>
<td><p>nested</p></td>
</tr>
<tr>
<td>normal</td>
<td>normal</td>
<td><p>nested</p></td>
</tr>
</table>
<h2>Direct attachment</h2>
<p><span>row index: </span><span id="direct-idx">unknown</span></p>
<button class="add-row" data-action="add-row" data-table="table-direct">Add row to direct</button>
<table id="table-direct" class="table">
<tbody>
<tr>
<td>normal</td>
<td>normal</td>
<td>normal</td>
</tr>
<tr>
<td><p>nested</p></td>
<td><p>nested</p></td>
<td><p>nested</p></td>
</tr>
<tr>
<td>normal</td>
<td>normal</td>
<td><p>nested</p></td>
</tr>
</tbody>
</table>
Here's the demo on jsFiddle.
P.S:
If you do have nested tables (or, in the general case, wish to delegate to elements with specific depth), you can use this suggestion from the jQuery bug report.
A simple and jQuery free solution:
document.querySelector('#elitable').onclick = function(ev) {
// ev.target <== td element
// ev.target.parentElement <== tr
var index = ev.target.parentElement.rowIndex;
}
Bonus: It works even if the rows are added/removed dynamically
You can use object.rowIndex property which has an index starting at 0.
$("table tr").click(function(){
alert (this.rowIndex);
});
See a working demo
$('tr').click(function(){
alert( $('tr').index(this) );
});
For first tr, it alerts 0. If you want to alert 1, you can add 1 to index.
In some cases we could have a couple of tables, and then we need to detect click just for particular table. My solution is this:
<table id="elitable" border="1" cellspacing="0" width="100%">
<tr>
<td>100</td><td>AAA</td><td>aaa</td>
</tr>
<tr>
<td>200</td><td>BBB</td><td>bbb</td>
</tr>
<tr>
<td>300</td><td>CCC</td><td>ccc</td>
</tr>
</table>
<script>
$(function(){
$("#elitable tr").click(function(){
alert (this.rowIndex);
});
});
</script>
DEMO
Related
This question already has answers here:
Event binding on dynamically created elements?
(23 answers)
Closed 5 years ago.
I am trying to get the value of table row using jQuery. When I try the same jQuery code on console and after I try to click, that is selecting that row and give proper value what I expect. But I don't know why it is not running from my JavaScript file. But the other functions are working on the same JavaScript file
$(".chequeTable tbody tr").click(function() {
alert('Vendor ID ==');
$('.selected').removeClass('selected');
$(this).addClass("selected");
alert('Vendor ID ==' + $('.tdVendorID').html());
$(this).css("background-color", "#000000");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table class="table table-hover FilterTable chequeTable table-striped" id="queryTable">
<thead>
<tr>
<th>S.No</th>
<th>VendorID</th>
<th>VendorName</th>
<th>ReqNo</th>
<th>ChequeAmount</th>
<th>VendorBalance</th>
<th>Balance</th>
<th>Description</th>
<th>StockValue</th>
<th>Yes/No</th>
</tr>
</thead>
<tbody>
<tr class="">
<td class="tdSno">1</td>
<td class="tdVendorID">6000 </td>
<td class="tdVendorName">john</td>
</tr>
</tbody>
</table>
Most probably the table is not there when you initialize the click event in jquery. Do you build the table dynamically?
Try to bind the click event to the table instead of the tr directly:
$(".chequeTable").on("click", "tr", function() { ... });
This only works when the table.chequeTable is in the HTML.
Some explanation why:
jquery documentation for on()
It says that it binds the events to all the elements in the selector. That means, elements that are not in the selector (any tr that is not yet present for example) won't receive the event. For that you have to bind the click Event on a container and check what the clicked subelement is. This is more perfomant anyways because you only attach one click Event and not 100s (if you have 100s tr-Elements)
Here you go with a solution
$("table.chequeTable").on('click', 'tr', function() {
alert('Vendor ID ==');
$('.selected').removeClass('selected');
$(this).addClass("selected");
alert('Vendor ID ==' + $('.tdVendorID').html());
$(this).css("background-color", "#000000");
});
$('tbody').append(`<tr class="">
<td class="tdSno">1</td>
<td class="tdVendorID">6000 </td>
<td class="tdVendorName">john</td>
</tr>`);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table class="table table-hover FilterTable chequeTable table-striped" id="queryTable">
<thead>
<tr>
<th>S.No</th>
<th>VendorID</th>
<th>VendorName</th>
<th>ReqNo</th>
<th>ChequeAmount</th>
<th>VendorBalance</th>
<th>Balance</th>
<th>Description</th>
<th>StockValue</th>
<th>Yes/No</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
You need to do event delegation as your row are generated dynamically.
Reference Documentation: Event delegation
Hope this will help you.
How do you get the id from a row element that has been dynamically created. Below is the code that I have tried but it will not fire the event.
HTML
<table id="tblRawMaterials" cellpadding="0" cellspacing="0">
<thead>
<tr>
<th>
A
</th>
<th>
B
</th>
<th>
C
</th>
</tr>
</thead>
<tbody>
<tr id="DCB4325E-951C-67E3-1E8F-7270D488A1EB" >
<td>G20002</td>
<td>1,783</td>
<td>2,000</td>
</tr>
</tbody>
</table>
Script
$("#tblRawMaterials tbody").click( function(e) {
// Here i need to capture row id of clicked cell.
});
Thanks in advance
you need the equivalent (old) sintax of on, exactly the same concept:
$("#tblRawMaterials tbody").delegate('tr', 'click', function(e) {
console.log($(this).attr('id'))
});
You need to use event delegation, in this way the event is attached to the parent tbody and fired anytime that a tr is clicked. It doesn't depend on the tr, so it will work also for elements dynamically added.
Please check che fiddle
You can try like this also with closest
$(document).ready(function() {
$("#tblRawMaterials tbody").click(function(e) {
var $tr = $(e.target).closest('tr'),
rowId = ($tr).attr("id"); // Here you can capture the row id of clicked cell.
alert(rowId);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<table id="tblRawMaterials" cellpadding="0" cellspacing="0">
<thead>
<tr>
<th>
A
</th>
<th>
B
</th>
<th>
C
</th>
</tr>
</thead>
<tbody>
<tr id="DCB4325E-951C-67E3-1E8F-7270D488A1EB">
<td>G20002</td>
<td>1,783</td>
<td>2,000</td>
</tr>
</tbody>
</table>
UPDATE :
If you really need delegate, then you can use the following code snippets.
$(document).ready(function () {
$(document).delegate("#tblRawMaterials tbody", "click", function (e) {
var $tr = $(e.target).closest('tr'),
rowId = ($tr).attr("id"); // Here you can capture the row id of clicked cell.
alert(rowId);
});
});
Note: Here I didn't bind click event for tr element.
Hope this helps!
You need to use event delegation, in this way the event is attached to the parent tbody and fired anytime that a tr is clicked. It doesn't depend on the tr, so it will work also for elements dynamically added.
$("#tblRawMaterials tbody").on('tr','click',function(e) {
var id = $(this).attr('id');
});
use jQuery delegate
Description: Attach a handler to one or more events for all elements that match the selector, now or in the future, based on a
specific set of root elements.
$("#tblRawMaterials tbody").delegate('tr','click',function(e) {
var id = $(this).attr('id');
});
try this
$(document).ready(function () {
$('#tblRawMaterials tr').click(function () {
alert($(this).attr('id'));
});
});
I have written a code where i need to trigger a functionality when mouse is over to specific elements.
Its working fine for below code for all static
$("table td").on("mouseenter",function(){
console.log("mouse entered")
});
but for all dynamic <td class="editcolumn">XYZ</td> event is not triggering even if i use below code
$("table td").on("mouseenter",".editcolumn",function(){
console.log("mouse entered")
});
Any idea how to make it work. I'm using jQuery 1.11
I know I just commented, but I figured I would show you an example:
HTML:
<table id="tablee" cellspacing="0">
<thead>
<tr class="tablehead">
<th>This</th>
<th>is</th>
<th>a</th>
<th>table</th>
<th>header</th>
<th>and</th>
<th>stuff</th>
<th>though</th>
</tr>
</thead>
<tr class="append">
<td class="edit-column">asdf</td>
<td class="edit-column">qwer</td>
<td class="edit-column">zxcv</td>
<td class="edit-column">rtyu</td>
<td class="edit-column">tyui</td>
<td class="edit-column">dfgh</td>
<td class="edit-column">dfgh</td>
<td class="edit-column">wert</td>
</tr>
<!-- ... -->
</table>
<br/>
<br/>
<input type="button" class="add-column" value="Add Column" />
Javascript:
$(function() {
$('.add-column').click(function() {
$('.append').append("<td class='edit-column'>iueo</td>");
$('.tablehead').append("<th>Mo columns</th>");
});
/* vvv - this */
$("#tablee").on('mouseenter', '.edit-column', function() {
/* ^^^ should be children or a child of this */
$(this).css('background-color', 'yellow');
});
});
Here is a fiddle
This question's answer gives a much better explanation of delegated events.
I too faced a similar problem for dynamic elements that are added or removed. In such situations you can create dynamic elements with event-handlers attached to their attributes i.e. in your case you can put the desired operations in a function which gets called by your attribute event handlers:
It should be something like this:
Javascript:
function whenMouseEnters() {
// perform some operations
}
Html:
<td onmouseenter="whenMouseEnters()">
If table is aviable on DOM load you can write delgated event for the td with class editColumn like this:
$("table").on("mouseenter",".editcolumn",function(){
console.log("mouse entered")
});
This is the HTML:
<table id="tblTestAttributes">
<thead>
<tr> <th>Head 1</th> <th>Head 2</th> </tr>
</thead>
<tbody>
<tr> <td id="txtDesc">Item 1</td> <td id="ddlFreq">Assume a DropDownList Here</td> </tr>
<tr> <td id="txtDesc">Item 1</td> <td id="ddlFreq">Assume a DropDownList Here</td> </tr>
<tr> <td id="txtDesc">Item 1</td> <td id="ddlFreq">Assume a DropDownList Here</td> </tr>
</tbody>
</table>
This is the javascript to get the values of each row:
var frequencies = [];
if ($('#tblTestAttributes').length) {
$('#tblTestAttributes tr').each(function () {
var t = $(this).find('td[id^="txtDesc"]').text() + ";" + $(this).find('[id^="ddlFreq"] option:selected').val();
alert(t);
frequencies.push(t);
});
}
I want to avoid the first row, which contains th elements which are just display headers and don't contain any data.
So I changed the selector to this:
#tblTestAttributes tr:not(:first-child)
This is skipping the second tr as well. What is happening here?
Simple you can use below code
$('#tblTestAttributes tr:not(:has(th))').each(function () {
In terms of performance, using .find() will be better than resolving the selector with Sizzle.
$('#tblTestAttributes').find('tbody').find('tr').each(function () { ... });
Here's the jsPerf to show it.
use
#tblTestAttributes tr:gt(0)
or
#tblTestAttributes tbody tr
I would recommend the 2nd, because it may take advantage of querySelectorAll and should be the fastes solution.
your approach didn't work as expected, because the 2nd tr is also a first-child(of tbody)
Use tr + tr selector, which gets all tr that appear after another tr, so the first one is skipped.
Also no need to check if table exists, as in that case $.each wouldn't even get executed.
var frequencies = [];
$('#tblTestAttributes tr + tr').each(function () {
var t = $(this).find('td[id^="txtDesc"]').text() + ";" + $(this).find('[id^="ddlFreq"] option:selected').val();
alert(t);
frequencies.push(t);
});
After your edit:
Simply select only all tr inside tbody:
$('#tblTestAttributes tbody tr').each(function(){
...
}
It happens because the second row is, in fact, the first child of the tbody just like the first row is the first child of the thead.
To only take the elements you need, I'd suggest something nearer from your need :
#tblTestAttributes tr:has(td)
Don't forget to get rid of those duplicate txtDesc id, this is illegal in HTML, use a class instead.
I have a question, I am trying to make some manipulation with html tables. I have two tables,
and when I hover first row from the first table, it should highlight both rows from both tables.
I have found a solution, in making this simple function:
<script type="text/javascript">
function matchrow(){
document.getElementById('row1').style.backgroundColor='#f5f5f5';
}
function unmatchrow(){
document.getElementById('row1').style.backgroundColor='white';
}
</script>
On the first table I have:
<tr onmouseover="matchrow()" onmouseout="dismatchrow()" >
on the second table I have:
<tr id="row1" >
So when I put mouseover the first row from the first table, the first row from the second table highlights.
My question is, how to make it for the every single row, especially if it will be dynamic table.
Hope I was clear.
I've implemented this with jQuery. It doesn't use obtrusive JS and doesn't require additional IDs for rows.
Also, CSS classes are more preferable than inline styles.
HTML:
<table id="t1">
<tr><td>......</td></tr>
<tr><td>......</td></tr>
</table>
<br/>
<table id="t2">
<tr><td>......</td></tr>
<tr><td>......</td></tr>
</table>
CSS:
tr.active > td
{
background-color:#f00;
}
JS:
$(function(){
$("#t1 tr").hover(
function(){
$(this).addClass('active');
$('#t2 tr:eq(' + $('#t1 tr').index($(this)) + ')').addClass('active');
},
function(){
$(this).removeClass('active');
$('#t2 tr:eq(' + $('#t1 tr').index($(this)) + ')').removeClass('active');
}
);
});
Here is live fiddle: http://jsfiddle.net/keaukraine/KBEhA/1/
You can use the div id as a parameter in the function
<tr onmouseover="matchrow('row1')" onmouseout="dismatchrow('row1')">
function matchrow(divid){
document.getElementById(divid).style.backgroundcolor='#F5F5F5';
}
function dismatchrow(divid){
document.getElementById(divid).style.backgroundcolor='white';
}
You can use jQuery for this.
Use the .eq() and .index() functions.
A way of doing it:
HTML:
<table border="1">
<tr>
<td>Row1</td>
</tr>
<tr>
<td>Row2</td>
</tr>
<tr>
<td>Row3</td>
</tr>
<tr>
<td>Row4</td>
</tr>
</table>
<table border="1">
<tr>
<td>Row1</td>
</tr>
<tr>
<td>Row2</td>
</tr>
<tr>
<td>Row3</td>
</tr>
</table>
JS:
$('table tr').hover(function()
{
var index = $(this).index();
$('table').each(function()
{
$(this).find('tr').eq(index).css('color', 'red');
});
});
A working example can be found here.