5

in the following table:

<table>
    <thead>
        <tr>
            <th>Th1</th>
            <th colspan='2'>Th23</th>
            <th>Th4</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Td1</td>
            <td>Td2</td>
            <td>Td3</td>
            <td>Td4</td>
       </tr>
    </tbody>
</table>

For the table cell containing text "Th23", I'd like to know which cells reside beneath it. In this case, the answer would be the cells containing text "Td2", and "Td3" respectively.

Are there any DOM properties or built-ins that help with this type of calculation?


@Matt McDonald has a more general solution.

This is what I ended up with:

// get tbody cell(s) under thead cell (first arg)
// if rowIndex===undefined, get from all rows; otherwise, only that row index
// NOTE: does NOT work if any cell.rowSpan != 1
var columnCells = function( th, rowIndex ) {
    // get absolute column for th
    for( var absCol=0, i=0; true; i++ ) {
            if( th.parentNode.cells[i] == th ) break;
            absCol += th.parentNode.cells[i].colSpan;
    }
    // look in tBody for cells; all rows or rowIndex
    var tBody = th.parentNode.parentNode.nextSibling;
    var cells = [];
    for( var r=((rowIndex==undefined)?0:rowIndex); true; r++ ) {
            if( rowIndex!==undefined && r>rowIndex ) break;
            if( rowIndex==undefined && r>=tBody.rows.length ) break;
            for( var c=0; true; c+=tBody.rows[r].cells[c].colSpan ) {
                    if( c < absCol ) continue;
                    if( c >= absCol+th.colSpan ) break;
                    cells.push(tBody.rows[r].cells[c]);
            }
    }
    return cells;
}
cc young
  • 18,939
  • 31
  • 90
  • 148
  • you could do this with jquery have a look here http://stackoverflow.com/questions/3523770/how-can-i-get-the-corresponding-table-header-th-from-a-table-cell-td it is not exactly what you are looking for but it very similar! peace – kasper Taeymans Sep 15 '11 at 20:02
  • @kasper The question you linked to is similar, but it supposes there are no colspans in the html table. But here you can't just use `index()` to compute a column index. That's the tricky part, but it's also on SO (see my answer for details). –  Sep 15 '11 at 20:17
  • FYI: Tables have a `tBodies` HTMLCollection. –  Sep 16 '11 at 15:09

2 Answers2

1

Right off the bat, you need to do three things:

  1. Give the table an id attribute for easy selection.
  2. Give the target cell an id attribute for easy selection as well.
  3. Select the cell's parentNode (row)

These three things will enable easier table-related calculations.

Next up is a function that grabs pseudo-properties of the specified cell. In this case, we're looking for its "start index" (in terms of columns), its "end index" (in terms of columns), and its "width" (end - start, in columns as well).

From there, you can traverse through the table's rows and check which cells fall between the start and the end indexes.

HTML:

<table id="foo">
    <colgroup span="1">
    <colgroup span="2">
    <colgroup span="1">
    <thead>
        <tr>
            <th>foo</th>
            <th id="example" colspan="2">bar</th>
            <th>baz</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>bing</td>
            <td>bang</td>
            <td>boom</td>
            <td>bong</td>
        </tr>
    </tbody>
</table>

JS (bear with me):

function getCellSpanProps(table, row, cell)
{
    var isRow = (function()
    {
        var i = 0, currentRow;
        for(i;i<table.rows.length;i++)
        {
            currentRow = table.rows[i];
            if(currentRow === row)
            {
                return true;
            }
            currentRow = null;
        }
        return false;
    }()), 
    cellHasCorrectParent, i = 0, 
    currentCell, colspanCount = 0,
    props;
    if(isRow)
    {
        cellHasCorrectParent = (function()
        {
            return cell.parentNode === row;
        }());
        if(cellHasCorrectParent)
        {
            for(i;i<row.cells.length;i++)
            {
                currentCell = row.cells[i];
                if(currentCell === cell)
                {
                    props = {"start": colspanCount, 
                    "end": colspanCount + cell.colSpan, 
                    "width": (colspanCount + cell.colSpan) - colspanCount};
                    break;
                }
                colspanCount += currentCell.colSpan;
                currentCell = null;
            }
            row = null;
        }
        return props;
    }
}

function findCellsUnderColumn(table, props)
{
    var i = 0, j = 0, row, cell,
    colspanCount = 0, matches = [],
    blacklist = {"": true, "NaN": true, "null": true, "undefined": true, 
    "false": true};
    if(blacklist[props.start] || blacklist[props.end] || blacklist[props.width])
    {
        return false;
    }
    for(i;i<table.rows.length;i++)
    {
        row = table.rows[i];
        colspanCount = 0;
        for(j=0;j<row.cells.length;j++)
        {
            cell = row.cells[j];
            if(colspanCount >= props.start && colspanCount < props.end)
            {
                matches.push(cell);
            }
            colspanCount += cell.colSpan;
            cell = null;
        }
        row = null;
    }
    return matches;
}

var table = document.getElementById("foo"), 
example = document.getElementById("example"),
targetRow = example.parentNode,
props = getCellSpanProps(table, targetRow, example),
matches = findCellsUnderColumn(table, props);
console.log(matches);

Demo: http://jsbin.com/ohohew/edit#javascript,html

This will determine which cells reside inside the particular column you're looking for (including the example). You can customize the function to fit your needs if that's not exactly what you're looking for.

  • your use of `colgroup` very nice! (had stopped using them several years ago because they did so little, but...) – cc young Sep 16 '11 at 06:40
  • Colgroups are a great way of explicitly marking up columns. Columns themselves tend to get overlooked, but colgroups (and cols) enable you to style them. –  Sep 16 '11 at 15:03
0

You need to know the column index of your cell. I'll name it ci. Then read its colspan (if empty, set it to 1). Then find the cells on the next line that have a column index >= ci and < ci + colspan. For such a complex need, using a JS framework is very useful. I'll suppose you can use JQuery, since it's the most frequently used.

Computing the colum index has several solutions on SO.

Reading the colspan attribute is just cell.attr('colspan') with jQuery.

Finding the next row is cell.closest('tr').next('tr').

The last step is to iterate over every element of the line and compute their column index. You could use the same function as above, but if it's not efficient enough, it should be easy to adapt its code so that it does not return an integer, but add elements to an array.

Community
  • 1
  • 1
  • thanks - can certainly iterate through cells as you suggest - just hoping there might be some strange dom property like `absoluteCellIndex` I was overlooking – cc young Sep 15 '11 at 20:35
  • You don't need jQuery for this. The DOM's table API is stunningly in-depth. –  Sep 15 '11 at 21:13