Given the following table, how would I get the corresponding table header for each td element?
<table>
<thead>
<tr>
<th id="name">Name</th>
<th id="address">Address</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bob</td>
<td>1 High Street</td>
</tr>
</tbody>
</table>
Given that I currently have any of the td
elements available to me already, how could I find the corresponding th
element?
var $td = IveGotThisCovered();
var $th = GetTableHeader($td);
var $th = $td.closest('tbody').prev('thead').find('> tr > th:eq(' + $td.index() + ')');
Or a little bit simplified
var $th = $td.closest('table').find('th').eq($td.index());
var $th = $("table thead tr th").eq($td.index())
It would be best to use an id to reference the table if there is more than one.
Solution that handles colspan
I have a solution based on matching the left edge of the td
to the left edge of the corresponding th
. It should handle arbitrarily complex colspans.
I modified the test case to show that arbitrary colspan
is handled correctly.
Live Demo
JS
$(function($) {
"use strict";
// Only part of the demo, the thFromTd call does the work
$(document).on('mouseover mouseout', 'td', function(event) {
var td = $(event.target).closest('td'),
th = thFromTd(td);
th.parent().find('.highlight').removeClass('highlight');
if (event.type === 'mouseover')
th.addClass('highlight');
});
// Returns jquery object
function thFromTd(td) {
var ofs = td.offset().left,
table = td.closest('table'),
thead = table.children('thead').eq(0),
positions = cacheThPositions(thead),
matches = positions.filter(function(eldata) {
return eldata.left <= ofs;
}),
match = matches[matches.length-1],
matchEl = $(match.el);
return matchEl;
}
// Caches the positions of the headers,
// so we don't do a lot of expensive `.offset()` calls.
function cacheThPositions(thead) {
var data = thead.data('cached-pos'),
allth;
if (data)
return data;
allth = thead.children('tr').children('th');
data = allth.map(function() {
var th = $(this);
return {
el: this,
left: th.offset().left
};
}).toArray();
thead.data('cached-pos', data);
return data;
}
});
CSS
.highlight {
background-color: #EEE;
}
HTML
<table>
<thead>
<tr>
<th colspan="3">Not header!</th>
<th id="name" colspan="3">Name</th>
<th id="address">Address</th>
<th id="address">Other</th>
</tr>
</thead>
<tbody>
<tr>
<td colspan="2">X</td>
<td>1</td>
<td>Bob</td>
<td>J</td>
<td>Public</td>
<td>1 High Street</td>
<td colspan="2">Postfix</td>
</tr>
</tbody>
</table>
You can do it by using the td's index:
var tdIndex = $td.index() + 1;
var $th = $('#table tr').find('th:nth-child(' + tdIndex + ')');
Pure JavaScript's solution:
var index = Array.prototype.indexOf.call(your_td.parentNode.children, your_td)
var corresponding_th = document.querySelector('#your_table_id th:nth-child(' + (index+1) + ')')
That's simple, if you reference them by index. If you want to hide the first column, you would:
Copy code
$('#thetable tr').find('td:nth-child(1),th:nth-child(1)').toggle();
The reason I first selected all table rows and then both td's and th's that were the n'th child is so that we wouldn't have to select the table and all table rows twice. This improves script execution speed. Keep in mind, nth-child()
is 1
based, not 0
.