fearless_fool
fearless_fool

Reputation: 35169

A CSS selector or xpath to iterate over rows with specific attributes

Imagine I am given a table like this:

<table>
  <tr><td>A</td></tr>
  <tr><td><a href="#">B</a></td></tr>
  <tr><td><a href="#">C</a></td></tr>
  <tr><td>D</td></tr>
  <tr><td><a href="#">E</a></td></tr>
  <tr><td>B</td></tr>
</table>

I'd like to construct a CSS selector (preferred) or an XPath (accepted) that picks out the n'th row that contains an a anchor, such that:

selector(1) => <a href="#">B</a>
selector(2) => <a href="#">C</a>
selector(3) => <a href="#">E</a>

CSS selectors

At this point, I'm pretty sure that CSS won't do the job, but

'table tr:nth-child(' + n + ')'

will pick out the n'th row, but that selects rows whether or not they have an a anchor. Similarly,

'table tr:nth-child(' + n + ') a'

will pick out rows with an a anchor, but only if n is 2, 3 or 5.

XPath

With XPath, this matches all the tr that have an a

`//table//tr//a/ancestor::tr`

but I can't figure out how to select the n'th match. In particular,

`//table//tr//a/ancestor::tr[position() = 2]`

doesn't appear to select anything.

Upvotes: 2

Views: 1443

Answers (3)

fearless_fool
fearless_fool

Reputation: 35169

Answers from @BoltClock and @StuartLC both work. But now that I know parentheses in XPath can control operator precedence, a more straightforward solution seems to be:

(//table//tr//a)[2]

Am I missing something?

Upvotes: 0

BoltClock
BoltClock

Reputation: 723548

You can't do this with a CSS selector1 for a number of reasons:

Your XPath is incorrect because a/ancestor::tr[position() = 2] returns the second tr ancestor of the a element. That is, the [position() = 2] predicate is connected to the ancestor:: axis. This XPath would match the middle-level tr in the following HTML:

<table>
  <tr><td><table>
    <tr><td><table>
      <tr><td><a href="#"></a>
    </table>
  </table>
</table>

In your HTML, each a element has only one tr ancestor, so this will not select anything.

The XPath you should use is:

(//table//tr[descendant::a])[2]

This matches the second tr element that contains an a descendant.


1 In Selectors 4, a potential solution would be table tr:nth-match(2 of :has(a)).

Upvotes: 2

StuartLC
StuartLC

Reputation: 107247

If I understand you correctly, you can find the nth td which has an <a href like so (you want C to be the 2nd match?):

(/table//tr/td[a[@href]])[2]

If you can't guarantee a td element, you can wild card the path and elements:

(/table//tr//*[a[@href]])[2]

Upvotes: 1

Related Questions