Reputation: 1799
I have a HTML table that contains both ROWSPANs and COLSPANs.
How can I find each cell's "visual location" using jQuery?
For example, here's a visual representation of my table with each cell populated with what the "visual location" algorithm should return:
(Note: I only really care about cells within the <tbody>
and the column reference can be an integer, not an alphabetic character, I've only done this to easily highlight the problem)
+--+--+--+--+--+
| |A |B |C |D |
+--+--+--+--+--+
|1 |A1|B1 |D1|
+--+--+--+--+ +
|2 |A2 |C2| |
+--+ +--+ +
|3 | |C3| |
+--+--+--+--+--+
|4 |A4|B4|C4|D4|
+--+--+--+--+--+
|XYZ |
+--+--+--+--+--+
I've tried implementing the first, however the reference for cell C3 is inaccurate as it doesn't take into account ROWSPANS. The second link may be able to be merged into the solution of the first, but I can't figure out how.
I'm hoping to use this as a function called getCellLocation(cell)
that will return an associative array that returns the location something like this:
function getCellLocation(cell)
{
var row_number = parseInt($(cell).parent().prevAll().length) + 1;
var col_number = 1;
$(cell).prevAll('td').each(function() {
col_number += $(this).attr('colspan') ? parseInt($(this).attr('colspan')) : 1;
});
var location = new Array();
location['row'] = row_number;
location['col'] = col_number;
location['index'] = $('td').index(cell) + 1;
return location;
}
$('table td').each(function(i){
var cell = getCellLocation($(this));
$(this).prepend('<span class="ref">R' + cell['row'] + ':C' + cell['col'] + ':D' + cell['index'] + '</span>');
});
Here's the HTML of the example table:
<table border="1" cellspacing="0">
<thead>
<tr>
<th></th>
<th>A</th>
<th>B</th>
<th>C</th>
<th>D</th>
</tr>
</thead>
<tbody>
<tr>
<th>1</th>
<td>A1</td>
<td colspan="2">B1</td>
<td rowspan="3">D1</td>
</tr>
<tr>
<th>2</th>
<td rowspan="2" colspan="2">A2</td>
<td>C2</td>
</tr>
<tr>
<th>3</th>
<td>C3</td>
</tr>
<tr>
<th>4</th>
<td>A4</td>
<td>B4</td>
<td>C4</td>
<td>D4</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="5">XYZ</td>
</tr>
</tfoot>
</table>
<style> span { background-color: #ffc; margin-right: .5em;} </style>
Upvotes: 11
Views: 4320
Reputation: 41
Since the accepted answer is missing a return
statement and only works with tbody and td (and not with thead and th) I did some slight adjustments to make it work:
function getCellLocation(cell) {
var cols = cell.closest("tr").children("th, td").index(cell);
var rows = cell.closest("thead, tbody").children("tr").index(cell.closest("tr"));
cell.prevAll("th, td").each(function() {
cols += ($(this).attr("colspan")) ? parseInt($(this).attr("colspan")) - 1 : 0;
});
cell.parent("tr").prevAll("tr").each(function() {
//get row index for search cells
var rowindex = cell.closest("thead, tbody").children("tr").index($(this));
// assign the row to a variable for later use
var row = $(this);
row.children("th, td").each(function() {
//check if this cell comes before our cell
if (cell.offset().left > $(this).offset().left) {
// check if it has both rowspan and colspan, because the single ones are handled before
var colspn = parseInt($(this).attr("colspan"));
var rowspn = parseInt($(this).attr("rowspan"));
if (rowspn && rowindex + rowspn > rows) {
cols += colspn ? colspn : 1;
}
}
});
});
return cols;
}
Upvotes: 0
Reputation: 31997
your solution my proposed table design
+--+--+--+--+--+ +--+--+--+--+--+
| |A |B |C |D | | |A |B |C |D | length 5 vs 5
+--+--+--+--+--+ +--+--+--+--+--+
|1 |A1|B1 |D1| |1 |A1|B1|//|D1|
+--+--+--+--+ + +--+--+--+--+--+
|2 |A2 |C2| | |2 |A2|//|C2|//| length 3 vs 5
+--+ +--+ + +--+--+--+--+--+
|3 | |C3| | |3 |//|//|C3|//|
+--+--+--+--+--+ +--+--+--+--+--+
|4 |A4|B4|C4|D4| |4 |A4|B4|C4|D4|
+--+--+--+--+--+ +--+--+--+--+--+
|XYZ | |XY|//|//|//|//| length 1 vs 5
+--+--+--+--+--+ +--+--+--+--+--+
// cells labeled '//' above have this class
td.stuffing { display: none; }
Do you see what I did there?
This is the third row, for example:
<tr>
<th>2</th>
<td rowspan="2" colspan="2">A2</td>
<td class="stuffing"></td>
<td>C2</td>
<td class="stuffing"></td>
</tr>
Now the function to retrieve correct index is very simple.
function getCellLocation(cell) {
return {row:cell.parentNode.rowIndex, cell:cell.cellIndex}
}
function getSmartCell(table, row, col) {
var cell = table.rows[row].cells[col];
if (cell.className.indexOf("stuffing") == -1) return cell;
// traverse Left
while ((row>0) && (col>0) && (cell.className.indexOf("stuffing") >= 0)) {
cell = table.rows[row].cells[--col];
}
// roll back one cell if no colSpan is found
if (cell.colSpan == 1) cell = table.rows[row].cells[++col];
// traverse Up
while ((row>0) && (col>0) && (cell.className.indexOf("stuffing") >= 0)) {
cell = table.rows[--row].cells[col];
}
return cell;
}
Usage:
var tb = document.querySelector("table");
getCellLocation(getSmartCell(tb,3,2)); // {row: 2, cell: 1}
ATTENTION
I have just checked the code of getSmartCell
and it returns wrong result if there are two neighbouring rowspans. I need to fix this.
Here is an example https://jsfiddle.net/goua3m13/
Upvotes: 1
Reputation: 66
Here a jQuery solution that is able to handle complex table structure. This solution use the Open Source WxT Table Parser available here: https://raw.github.com/wet-boew/wet-boew/master/src/js/dependencies/parserTable.js
You will find the documentation in the Table Usability Concept Github Repository. Use the API documentation that is under "Table Parser - WET 3.0 release".
The table parsing is done by using the HTML tabular markup and the visual relationships between the data cell (td) and the header cell (th)
// Used to get the reference to the WxT table parser
var _pe = window.pe || {
fn : {}
};
// For each table elements
$('table').each(function () {
var $tbl = $(this);
// Parse the table
_pe.fn.parsertable.parse($tbl);
// For each data cell
$('td', $tbl).each(function () {
var $td = $(this),
tblparser;
// Get the API structure as defined under "Table Parser - WET 3.0 release" (https://github.com/duboisp/Table-Usability-Concept/blob/master/API/td.md#table-parser---wet-30-release)
tblparser = $td.data().tblparser;
// Set the cell location (x, y)
$td.html(tblparser.row.rowpos + ', ' + tblparser.col.start);
});
});
You will find a working example here: http://jsfiddle.net/TVttA/
Cheers
:-)
Upvotes: 0
Reputation: 15616
Here's my solution:
function getCellLocation(cell) {
var cols = cell.closest("tr").children("td").index(cell);
var rows = cell.closest("tbody").children("tr").index(cell.closest("tr"));
var coltemp = cols;
var rowtemp = rows;
cell.prevAll("td").each(function() {
cols += ($(this).attr("colspan")) ? parseInt($(this).attr("colspan")) - 1 : 0;
});
cell.parent("tr").prevAll("tr").each(function() {
//get row index for search cells
var rowindex = cell.closest("tbody").children("tr").index($(this));
// assign the row to a variable for later use
var row = $(this);
row.children("td").each(function() {
// fetch all cells of this row
var colindex = row.children("td").index($(this));
//check if this cell comes before our cell
if (cell.offset().left > $(this).offset().left) {
// check if it has both rowspan and colspan, because the single ones are handled before
var colspn = parseInt($(this).attr("colspan"));
var rowspn = parseInt($(this).attr("rowspan"));
if (colspn && rowspn) {
if(rowindex + rowspn > rows)
cols += colspn;
}
if(rowspn && rowindex + rowspn > rows) cols +=1;
}
});
});
I'm checking for cells which have both colspan and rowspan, because the rest are handled by the first five lines of this code. If the cells have both rowspan and colspan, they should be effecting other cells that are not below OR aside this cell, so I need to search for every cell's previous cells for interaction.
Upvotes: 10
Reputation: 4209
for this specific layout (i.e. if your first row doesn't have colspan and more or less uniform cell borders) you may do it this way:
function getCellLocation(cell)
{
var row_number = cell.parentNode.rowIndex;
var col_number = "";
$(cell).parents('table').find('tr:first th').each( function()
{
if (cell.offsetLeft >= this.offsetLeft)
{
col_number = $(this).text();
}
else return false;
});
return col_number + row_number;
}
if you want numeric column change it to
var col_number = 0;
...
if (cell.offsetLeft >= this.offsetLeft)
{
col_number++;
}
Upvotes: 0