views:

54

answers:

4

I have a table with a "header" that user regular tr tags (not th). I need to find header "Col2" and then to each cell under Col2 add an anchor. I can do $("td:contains('Col2')) to find the header, but the data rows could also have "Col2". How would I search just the first row and then loop through the row cells?

<table>
  <tr>
    <td>Col1</td>
    <td>Col2</td>
  </tr>
  <tr>
    <td>Data1</td>
    <td>Data2</td>
  </tr>
  <tr>
    <td>Data3</td>
    <td>Data4</td>
  </tr>
</table>

Becomes:

<table>
  <tr>
    <td>Col1</td>
    <td>Col2</td>
  </tr>
  <tr>
    <td>Data1</td>
    <td><a href="?Data2">Data2</a></td>
  </tr>
  <tr>
    <td>Data3</td>
    <td><a href="?Data4">Data4</a></td>
  </tr>
</table>

Edit: I actually have more than one table in the same page. :first only matches the first row in the first table.

Update: Here is how I finally got it working. Thanks to everyone for your help! With a little from each of you and a little from the API docs it works. Now that I'm starting to get the hang of it, I can't imagine the pain of doing this without jQuery.

$('table').each(function(i, table) {
    $(table).find('tr:first > td:contains("Col2")').each(function() {
        var cellIndex = $(this).index() + 1;

        $(table).find('tr:not(:first) > td:nth-child(' + cellIndex + ')').wrapInner(function() {
            return $('<a />', { 'href': '?data=' + $(this).text() });
        });
    });
});
A: 

I would do the following:

search header row for column with matching criteria and then get column index. Then select all rows in the table starting from the second one that are found at that column index. Then create your anchors. I can try to write up some code if you need help with that.

spinon
I'm not sure on how I would loop through each. I tried doing `$("...").each(function() { How do I get the index here? });`
Nelson
@Nelson - If you went the `.each()` route, the index is the first argument passed to it, e.g. with this: `$("...").each(function(i, elem) {` `i` would be the index, `elem` would be the DOM element (same as `this` normally).
Nick Craver
`i` ends up being 0, even though it's the second column. If I replace `each(...)` with something like `css(...)`, it changes the correct cell. Any ideas how I can get the correct index this way?
Nelson
I see, i is 0 because it's the first matched item, not the index of the cell.
Nelson
+1  A: 

have you tried using the ":first" selector (including "not(:first)")

http://api.jquery.com/first-selector/

programatique
"td:first:contains('Col2')" doesn't work. What would be the correct syntax to aggregate the filters?
Nelson
`$('table > tbody > tr:first > td:contains("Code")')` is somewhat working. I have several tables and it's only picking up the first one.
Nelson
Similarly, `not(:first)` is picking up the first `tr` in other tables.
Nelson
+1  A: 

[See it in action]

var index = $("table:first-child td:contains('Col2')").index() + 1;
$("tr:not(:first) :nth-child("+index+")").each(function(){
   var old = $(this).html();
   $(this).html("<a href='?"+old+"'>"+old+"</a>");                                          
});

galambalazs
This doesn't match the example, the second one should be `Data4` :) It's per-cell, so you need to take that into account.
Nick Craver
i've updated while you were writing :)
galambalazs
After your edit, you should account for the possibility that there's a `'` in the old html. Also `.html()` can take a function in 1.4+, much cleaner to do it that way...though `.wrap()` is a much better/safer approach here.
Nick Craver
http://forkjavascript.org/ :)
galambalazs
Very similar to what Nick had.
Nelson
he answered just right
galambalazs
A: 

You can do it using .index() and .wrapInner(function) like this:

var i = $("table tr td:contains('Col2')").index() + 1;
$("table tr:gt(0) td:nth-child(" + i +")")​​​​​​​​​​​​​​​​​​​​​​​​​​​​​.wrapInner(function() {
    return $("<a />", { "href": "?" + $(this).text() });
});​

You can see an example here, this gets the index of the <td> that contains "Col2" (0-based) then uses the :nth-child() selector (1-based, so we add 1) to get the <td> elements you want to .wrapInner(). After that we're just returning the structure to wrap them in, generated via $(html, props).

Nick Craver
This actually wraps the whole cell: `<a ...><td>...</td></a>`, giving funny results. How would I wrap only the cell contents?
Nelson
@Nelson - That should have been a `.wrapInner()`, oversight on my part...answer/demo updated with the correct code :)
Nick Craver
I got some help from the others, but yours was the most helpful so I'm going to accept your answer. I still can't get this to work with multiple tables on the same page, though. I split it up into `$("table").each(function(i, elem) { $(elem).children("tr:first > td:contains('Col2')").text('hello') });` If I take out `.children(...)`, all the tables are replaced by 'hello'. With .children(...), only the first table works. I triple checked and all the tables have the same structure.
Nelson
Also, not sure how I can explain this, but `$(elem).children("tr:first").css('background-color', 'blue');` changes the background on the first tr. `$(elem).children("tr").css('background-color', 'blue');` doesn't do anything. I think my problem is doing $(elem).
Nelson
I'm still not sure how I can explain that, but my problem was using .children() instead of .find(). One detail I left out is that there was a <tbody> tag, so the <tr> was not a direct child. I have updated my question with my final solution.
Nelson