I'd like to be able to select a table cell by row and column indices, while pensating for rowspan
and colspan
. For example, in the following table,
I would expect coloring (1,2) red, (2,3) green, and (3,3) blue, to yield:
I tried the answer to the question, "Selecting an arbitrary cell in a table by row and column number", but this yielded:
Here is the jsFiddle: /
I understand what is happening, and I even found another question, "Table cellIndex and rowIndex with colspan/rowspan", where an answer was supplied as a plugin, but it just seems unbelievable that there isn't a simpler way! After all, the coloring of the actual result seems understandable, yet unintuitive, while the coloring of the expected result seems far more intuitive and easily graspable.
Can anyone think of a clever and simpler way to implement this?
Update
Here's a new jsFiddle with my (poor) attempt, in case it might inspire a new idea in someone else. Basically, if we assumed the row and col headers weren't spanned (which, of course, isn't a valid assumption), then we could use offsets to "target" the correct cell:
function getCell(table, r, c)
{
var rowHead = $(table.rows[r].cells[0]);
var colHead = $(table.rows[0].cells[c]);
var y = rowHead.offset().top + rowHead.outerHeight(true)/2;
var x = colHead.offset().left + colHead.outerWidth(true)/2;
return $(document.elementFromPoint(x, y));
}
While the demo appears to work, there are a number of problems:
- Can't assume row and col headers aren't spanned.
- Doesn't work if midpoint of row or col is off the viewport;
elementFromPoint
seems to depend on the viewport. - Doesn't work reliably when scrolling, margins, etc. e into play; flimsy in general; would rather not rely on coordinate-math.
I'd like to be able to select a table cell by row and column indices, while pensating for rowspan
and colspan
. For example, in the following table,
I would expect coloring (1,2) red, (2,3) green, and (3,3) blue, to yield:
I tried the answer to the question, "Selecting an arbitrary cell in a table by row and column number", but this yielded:
Here is the jsFiddle: http://jsfiddle/acheong87/27HuN/
I understand what is happening, and I even found another question, "Table cellIndex and rowIndex with colspan/rowspan", where an answer was supplied as a plugin, but it just seems unbelievable that there isn't a simpler way! After all, the coloring of the actual result seems understandable, yet unintuitive, while the coloring of the expected result seems far more intuitive and easily graspable.
Can anyone think of a clever and simpler way to implement this?
Update
Here's a new jsFiddle with my (poor) attempt, in case it might inspire a new idea in someone else. Basically, if we assumed the row and col headers weren't spanned (which, of course, isn't a valid assumption), then we could use offsets to "target" the correct cell:
function getCell(table, r, c)
{
var rowHead = $(table.rows[r].cells[0]);
var colHead = $(table.rows[0].cells[c]);
var y = rowHead.offset().top + rowHead.outerHeight(true)/2;
var x = colHead.offset().left + colHead.outerWidth(true)/2;
return $(document.elementFromPoint(x, y));
}
While the demo appears to work, there are a number of problems:
- Can't assume row and col headers aren't spanned.
- Doesn't work if midpoint of row or col is off the viewport;
elementFromPoint
seems to depend on the viewport. - Doesn't work reliably when scrolling, margins, etc. e into play; flimsy in general; would rather not rely on coordinate-math.
- 1 And what of more plex models? You are asking for something generic to solve every situation not just this one you say, so here is a worst case scenario example of the table: jsfiddle/27HuN/25 . – Travis J Commented Dec 27, 2012 at 21:53
- @TravisJ - You deserve half the accepted-answer rep just for creating the plex scenario to test against. Thanks. (Karma is ing your way ;-) – Andrew Cheong Commented May 4, 2013 at 5:10
3 Answers
Reset to default 4Here's a different approach which does 'pre-processing':
var grid = (function(){
var table = $("#table")[0], a=[], cell, i, j, k, l, y;
for (i=0;i<table.rows.length;i++) a[i] = [];
for (i=0;i<table.rows.length;i++) {
y = 0;
for (j=0;j<table.rows[i].cells.length;j++) {
while (a[i][j + y]) y++;
cell = $(table.rows[i].cells[j]);
xspan = parseInt(cell.attr('rowspan') || 1);
yspan = parseInt(cell.attr('colspan') || 1);
for (k=0;k<xspan;k++) {
for (l=0;l<yspan;l++) {
if(i + k < table.rows.length) a[i + k][j + y + l] = [i,j];
}
}
}
}
return a;
})();
colorCell(1,2,'red');
colorCell(2,3,'green');
colorCell(3,3,'blue');
function colorCell(i,j,s){
var a = grid[i][j];
$(table.rows[a[0]].cells[a[1]]).css('background-color', s);
}
jsfiddle
Your design is custom, and so your solution must also be custom. There is no simple generic way to approach this because of how departed the table is from the expected structure. Remove the mented out td
elements and you will see the true shape of what the script is dealing with.
As in all good optimization, you should do some work up front. Use class names or data-attributes to mark which cell is which when you prepare them, and the work afterwards will dramatically decrease. Doing no work up front will cost you later in the end, as you can see from costly calculations and workarounds such as the suggested plugin.
See this jsfiddle for an example of what I mean: http://jsfiddle/27HuN/2/
In case something happens to it, here is a copy
html
<table id="table" border="1" cellpadding="10" style="text-align:center;">
<tbody>
<tr class="0">
<td class="0-0">(0,0)</td>
<td class="0-1">(0,1)</td>
<td class="0-2">(0,2)</td>
<td class="0-3">(0,3)</td>
</tr>
<tr class="1">
<td class="1-0">(1,0)</td>
<td colspan="2" class="1-1 1-2">(1,1) (1,2)</td>
<!--<td></td>-->
<td rowspan="2" class="1-3 2-3">(1,3)<br/>(2,3)</td>
</tr>
<tr class="2">
<td class="2-0">(2,0)</td>
<td class="2-1">(2,1)</td>
<td class="2-2">(2,2)</td>
<!--<td></td>-->
</tr>
<tr class="3">
<td class="3-0">(3,0)</td>
<td class="3-1">(3,1)</td>
<td class="3-2">(3,2)</td>
<td class="3-3">(3,3)</td>
</tr>
</tbody>
</table>
js
$('.1-2').css('background-color', 'red');
$('.2-3').css('background-color', 'green');
$('.3-3').css('background-color', 'blue');
Note that this is a simple example, and you would probably want to use class names which were not just numbers. Perhaps r0c0
, r2c3
type of markup, or something more verbose to logically represent these positions.
I have tried it in JSFiddle... Took some time though...
var table = $("#table")[0];
SetColumnColor(1, 2, 'red');
SetColumnColor(2, 3, 'green');
SetColumnColor(3, 3, 'blue');
function SetColumnColor(rowIndex, cellIndex, color){
var actualRowIndex = GetActualRowIndex(table, rowIndex, cellIndex);
var actualCellIndex = GetActualCellIndex(table.rows[actualRowIndex], cellIndex);
$(table.rows[actualRowIndex].cells[actualCellIndex]).css('background-color', color);
}
function GetActualCellIndex(row, cellIndex) {
var actualCellIndex = cellIndex;
$(row.cells).each(function(index) {
var colSpan = parseInt($(this).attr('colspan'));
if(colSpan != NaN && colSpan > 0)
{
//alert(colSpan);
actualCellIndex = actualCellIndex - colSpan + 1;
}
});
return actualCellIndex;
}
function GetActualRowIndex(table, rowIndex, cellIndex) {
var actualRowIndex = rowIndex;
if(table.rows[rowIndex].cells[cellIndex] != null)
return rowIndex;
$(table.rows).each(function(index) {
if(rowIndex <= index)
return actualRowIndex;
var realCellIndex = GetActualCellIndex(this, cellIndex);
//alert("Row:" + index + "(" + realCellIndex + "," + cellIndex+ ")");
var rowSpan = parseInt($(table.rows[index].cells[realCellIndex]).attr('rowspan'));
if(rowSpan != NaN && rowSpan > 0)
{
//alert(rowSpan + "(" + index + "," + realCellIndex + ")");
actualRowIndex = actualRowIndex - rowSpan + 1;
}
});
return actualRowIndex;
}
A working demo is available here