Misleading cellIndex values with rowspan/colspan

M

Matt Kruse

Using the .cellIndex property of a TH element to find out which table column
it is over can cause misleading results when the table has cells which have
rowspans or colspans greater than 1.
See example: http://www.javascripttoolbox.com/temp/table_cellindex.html
(code pasted below for reference)

This behaves according to specs and expectations, however the value of the
cellIndex property becomes less useful when using it to determine which
column to sort onclick, etc.

I plan to write some code to calculate the "actual" column index of each
cell in a <thead> of a table. But if anyone has already done this exercise,
please do share so I can perhaps use my time working on something else :)

Thanks!

EXAMPLE CODE:

<html>
<head>
<title></title>
<script type="text/javascript">
window.onload = function() {
var ths = document.getElementsByTagName('TH');
for (var i=0; i<ths.length; i++) {
ths.innerHTML = ths.parentNode.rowIndex + "," + ths.cellIndex;
}
}
</script>
<style>
th {
vertical-align:top;
text-align:left;
width:50px;
}
</style>
</head>
<body>
<table border="1">
<thead>
<tr>
<th rowspan="2">&nbsp;</th>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>&nbsp;</th>
<th>&nbsp;</th>
</tr>
<tr>
<th colspan="2">&nbsp;</th>
<th rowspan="2">&nbsp;</th>
<th>&nbsp;</th>
</tr>
<tr>
<th colspan="2">&nbsp;</th>
<th>&nbsp;</th>
<th>&nbsp;</th>
</tr>
</thead>
</table>
</body>
</html>
 
C

Csaba Gabor

Matt said:
Using the .cellIndex property of a TH element to find out which table column
it is over can cause misleading results when the table has cells which have
rowspans or colspans greater than 1.
See example: http://www.javascripttoolbox.com/temp/table_cellindex.html
(code pasted below for reference)

This behaves according to specs and expectations, however the value of the
cellIndex property becomes less useful when using it to determine which
column to sort onclick, etc.

I plan to write some code to calculate the "actual" column index of each
cell in a <thead> of a table. But if anyone has already done this exercise,
please do share so I can perhaps use my time working on something else :)

Hiya Matt,

The good I have done this before. I think I did it because I
wanted a general routine such that when someone clicked on any table
cell, I could find every other cell in the same row or column.
The bad I don't know where I put it offhand.
More bad The situation is more complicated than your nice
example shows:

<table border bgcolor=blue>
<tr>
<td bgcolor=yellow>1x1</td>
<td bgcolor=purple colspan=2>2 wide</td>
<td rowspan=3 bgcolor=green valign=bottom>3 deep</td>
</tr>
<tr><td bgcolor=red>1x1</td></tr>
<tr><td colspan=2 bgcolor=pink>2 wide</td>
<td colspan=2>2 wide</td>
</tr>
</table>

In particular, as my example shows, multiple cells can overlap the same
"tile", and a given "tile" need not be overlapped by any cells at all!

So I might be able to advise you in this endeavor of yours. It seems
like you're after:

function colSpan(tableCell) {
// returns [leftCol,rightCol] that the tableCell corresponds to
// leftCol equals rightCol if colSpan of the tableCell is 1 }

If you can guarantee that you will only query on the top row of the
table, then this function is fairly straightforward because you get the
first row of the table
(tableCell.parentNode.parentNode.parentNode.rows[0]) and then you march
through the cells one by one determining the colSpan array of each
till you get to the tableCell in question.

function colSpan(topRowCell) {
// returns [leftCol,rightCol] that topRowCell corresponds to
// leftCol equals rightCol iff colSpan of the tableCell is 1
// function fails badly if topRowCell isn't in the table's top row
var topRow=topRowCell.parentNode.parentNode.parentNode.rows[0];
var priorRight = -1;
for (var i=0;topRow.cells!=topRowCell;++i)
priorRight += topRow.cells.colspan;
return [priorRight+1,priorRight+topRow.cells.colspan]; }


Disclaimer: I have not tested the above function, even for syntax
errors
If you can't make the top row guarantee, then it is much messier.

Csaba Gabor from Vienna
 
M

Matt Kruse

Csaba said:
In particular, as my example shows, multiple cells can overlap the
same "tile", and a given "tile" need not be overlapped by any cells
at all!

Overlapping of cells is, I believe, invalid.
If you can guarantee that you will only query on the top row of the
table

Which, of course, isn't possible.
If it were, then it would be trivial to find the true cellIndex value.
Colspans are easy to taken into consideration. Rowspans are more difficult,
and seem to require traversal through every row before and including the row
of the cell for which you want to find the true cellIndex.
 
R

Randy Webb

Matt Kruse said the following on 8/3/2006 4:43 PM:
Overlapping of cells is, I believe, invalid.

It is, and is only "honored" by IE by joining all the cells.
 
M

Matt Kruse

Matt said:
Colspans are easy to taken into consideration. Rowspans are more
difficult, and seem to require traversal through every row before and
including the row of the cell for which you want to find the true
cellIndex.

Well, I've thrown together a really messy proof-of-concept at the same url:
http://www.javascripttoolbox.com/temp/table_cellindex.html

It appears to work, so I'm going to clean it up, optimize it, and add it to
my table lib.
If anyone sees any problem with the general logic, please do critique.
I guarantee the "finished" version will be much prettier ;)
 
C

Csaba Gabor

Randy said:
Matt Kruse said the following on 8/3/2006 4:43 PM:

It is, and is only "honored" by IE by joining all the cells.

I don't really understand the above two comments.
I have tested the table that I gave on both IE 6 and FF 1.5 (and
similar tables on earlier versions a few years ago), and they exhibit
both the "hole" at tiles [1,1] and [1,2] (coordinates are 0 based) with
a blue color (the bgcolor of the table), and a tile overlapped by two
cells at [2,3].

Note that this latter tile shows the (overlapped) text of two cells.
Furthermore, if I had set a bgcolor on the last cell, then it would
have covered the green cell so that the effect would not have been
readily observable.

Csaba
 
C

Csaba Gabor

Matt said:
Which, of course, isn't possible.
If it were, then it would be trivial to find the true cellIndex value.
Colspans are easy to taken into consideration. Rowspans are more difficult,
and seem to require traversal through every row before and including the row
of the cell for which you want to find the true cellIndex.

OK, then here's the approach that I would take if I were to do this
again. I would build an associative array of all tiles and have the
value of each one be the cell that covers it (I am assuming that the
table is such that no two cells overlap the same tile - if so, you
could generalize my scheme). The index of tile at position [n,m] will
be n+"x"+m. Thus, the pseudo code is something like:

function nextFreeIdx(aTiles, rowNum, colStart, maxCol) {
for (;colStart<=maxCol;++colStart) // find first free index
if (!aTiles[rowNum+"x"+colStart]) break;
return colStart; }

function findTileToCellMap(table, terminatingCell) {
// pass in terminatingCell if you want to get a specific cell's info
// aTiles maps tile position to cell that covers it.
var i,j,csp,maxCol=-1, aTiles = [];
for (rowNum=0;rowNum<table.rows.length;++rowNum) {
var thisRow=table.rows[rowNum];
var colNum=-1;
for (cellNum=0;cellNum<thisRow.cells.length;++cellNum) {
thisCell=thisRow.cells[cellNum];
csp=thisCell.colspan;
colNum=nextFreeIdx(aTiles, rowNum, colNum+1, maxCol);
for (i=0;i<thisCell.rowspan;++i)
for (j=0;j<csp;++j)
aTiles[(thisRow+i)+"x"+(colNum+j)]=thisCell;
if (colNum+csp-1>maxCol) maxCol=colNum+csp-1;
if (thisCell==terminatingCell) return [rowNum, colNum]; // [top, left]
} }
return aTiles; }

To make this more usable, you should really affix to each cell it's top
left tile position as you compute it. That way, you can not only go
from the tile to the cell, but from the cell to the tile (which is what
was desired in the first place). One should be aware of possible
issues when cells are collapsed/combined (eg. table is 3 tiles high.
Cell at tile 0,3 is two by two, cell at tile 2,3 is two wide by 1 high
=> cols 3 and 4 are collapsed into one).

Csaba Gabor
 
C

Csaba Gabor

Matt said:
Well, I've thrown together a really messy proof-of-concept at the same url:
http://www.javascripttoolbox.com/temp/table_cellindex.html

It appears to work, so I'm going to clean it up, optimize it, and add it to
my table lib.
If anyone sees any problem with the general logic, please do critique.
I guarantee the "finished" version will be much prettier ;)

Seems to work pretty nicely. Nice job.
Since I sent in my suggestion before seeing that you'd already solved
your problem, I figured I may as well clean up my work, too. I've cast
it into the form that you are using. Just cut and paste and you should
see no difference :)


function findTileToCellMap(table, terminatingCell) {
// pass in terminatingCell if you want to get a specific cell's info
// aTiles maps tile position to cell that covers it.
var cellNum, colNum, aTiles = [];
for (var rowNum=0;rowNum<table.rows.length;++rowNum) {
var thisRow=table.rows[rowNum];
for (cellNum=0, colNum=-1;cellNum<thisRow.cells.length;++cellNum) {
var thisCell=thisRow.cells[cellNum];
while (aTiles[rowNum+"x"+ ++colNum]); // next free index
for (var i=0;i<(thisCell.rowSpan || 1);++i)
for (var j=0;j<(thisCell.colSpan || 1);++j)
aTiles[(rowNum+i)+"x"+(colNum+j)]=thisCell;
thisCell.tile = [rowNum, colNum]; // [top, left] - cache value
if (thisCell==terminatingCell) return thisCell.tile; } }
return aTiles; }


function getActualCellIndex(cell) {
if (cell.tile) return cell.tile[1]; // cached
var table = cell.parentNode.parentNode.parentNode;
findTileToCellMap (table); // cache values here
return cell.tile[1]; }


You could also implement getActualCellIndex as:

function getActualCellIndex(cell) {
// doesn't use cached values
var table = cell.parentNode.parentNode.parentNode;
return findTileToCellMap (table, cell)[1]; }

Csaba Gabor
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,053
Latest member
billing-software

Latest Threads

Top