views:

188

answers:

4

Consider the following table:

<table>
    <thead>
        <tr>
            <th scope="col" />
            <th scope="col">A</th>
            <th scope="col">B</th>
            <th scope="col">C</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <th scope="row">1</th>
            <td>Apples</td>
            <td>Oranges</td>
            <td>Pears</td>
        </tr>
        <tr>
            <th scope="row">2</th>
            <td rowspan="2">Subcategory Heading</td>
            <td>ASP.Net</td>
            <td>Other</td>
        </tr>
        <tr>
            <th scope="row">3</th>
            <td>MVC</td>
            <td>PHP</td>
        </tr>
        <tr>
            <th scope="row">4</th>
            <td>Red</td>
            <td>Green</td>
            <td>Blue</td>
        </tr>
    </tbody>
    <tfoot>
        <tr>
            <td colspan="4">Some pointless footer content</td>
        </tr>
    </tfoot>
</table>

How would I retrieve the correct column number for the cell containing the text "MVC" or "PHP" using jQuery? The correct answer would be column 3, but just using .prevAll().length will only count the number of previous cells, which will not accurately reflect their actual column position in this case?

I'm sure I'll need to use each(), but I'm looking for the most efficient implementation as well.

+1  A: 

Something like this?

<!doctype html>
<table id="foo">
    <thead>
        <tr>
            <th scope="col" />
            <th scope="col">A</th>
            <th scope="col">B</th>
            <th scope="col">C</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <th scope="row">1</th>
            <td>Apples</td>
            <td>Oranges</td>
            <td>Pears</td>
        </tr>
        <tr>
            <th scope="row">2</th>
            <td rowspan="2">Subcategory Heading</td>
            <td>ASP.Net</td>
            <td>Other</td>
        </tr>
        <tr>
            <th scope="row">3</th>
            <td>MVC</td>
            <td>PHP</td>
        </tr>
        <tr>
            <th scope="row">4</th>
            <td>Red</td>
            <td>Green</td>
            <td>Blue</td>
        </tr>
    </tbody>
    <tfoot>
        <tr>
            <td colspan="4">Some pointless footer content</td>
        </tr>
    </tfoot>
</table>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js"&gt;&lt;/script&gt;
<script>
    var table = $('table#foo');
    table.find('td[rowspan], th[rowspan]').each(function() {
    var cellNum,
        reference = this,
        columnNum = $(this).attr('rowspan'),
        parentTr = $(this).closest('tr'),
        parentTrs = $(this).closest('tr').parent().find('tr');

    parentTr.find('>td, >th').each(function(i,o) {
        if ( this == reference ) {
     cellNum = i;

     var counter = columnNum;

     while ( counter-- ) {
         $(this).closest('tr').next().find('>td,>th').each(function(i,o) {
      if ( cellNum == i ) {
          $(this).addClass('rowspan-affected');
          $(this).attr('colNum', columnNum);
      }
         });
     }
        }
    });

    /* testing
    window.num = num;
    window.parentTr = parentTr;
    window.parentTrs = parentTrs;
    */
    });
</script>
meder
Make sure there are enough rows or the while loop may be endless.
meder
Another note - I relied on 0 being the first ( and not 1 ) just because it's less math for me :)
meder
A: 

Well, the shortest (over)simplification is:

var theCell = $('td:contains("MVC")');
col = theCell.parent().children().index(theCell);
row = theCell.parent().parent().children().index(theCell.parent());

Note, the indexes returned are zero-based. Basically, first you find the cell of interest. For the column, you go up a level in the DOM to the <tr>, get all children (which in this case will be the descendant <td>s and the <th>s) and find the position of the element of interest in that set. For the row, you climb up to, in this case, the <tbody>, get all rows, and find the position of the row of the <td> of interest.

As you can imagine, changes in the structure of the table changes the possible results. However, if you programmatically use the indexes later the same way you got them, you can get back to the same cell. I bring this up because if you somehow use the indexes visually in the UI, you may have to get trickier and account for spans, <tbody> vs no <tbody>, etc...

Don't know if it is more efficient than meder's answer. But, it is definitely much more maintainable! :)

alphadogg
Ah, but this approach won't take into account a cell's absolute position if a previous row's cell has been spanned across multiple rows. Instead you'll get the cell's index relative to that spanned group (if that makes sense). In other words, this doesn't return the correct column number, just the order in which the cell appears within that row.
Phil.Wheeler
So, you want a "visual" index, for lack of better terminology? So, even though the "MVC" cell is second in the row (actually third since your prevAll() will also include the `<th>` cells), you want it to be seen as in column 3? IOW< you want to "virtually unmerge" the cells before the index?
alphadogg
Yeah, that's basically the idea.
Phil.Wheeler
A: 

I think the closest to shorten search steps would do something like this:

var counter = 0;
$("#idTable tbody tr").each(function(){

   var html = $(this).html();
   var php = html.indexOf("PHP") > -1 ? true : false;
   var mvc = html.indexOf("MVC") > -1 ? true : false;

   if(php || mvc) {
       $(this).find("td").each(function(){ counter += (($(this).html()=="PHP") || ($(this).html()=="MVC")) ? 1 : 0; });
   }

});
andres descalzo
A: 

Can't you just use the cellIndex in Javascript?

alert( $('td:contains("PHP")')[0].cellIndex );
fudgey