Mike
Mike

Reputation: 58

how to find the preceding sibling of a link

I have the following I am trying to analyse using xpath

<table>
 <tr>

  <td>Name</td>
  <td>Info</td>
  <td><a href = "file1">Download</a></td>

</tr>

<tr>
 <td>Name2</td>
 <td>Info</td>
 <td><a href = "file2">Download</a></td>

</tr>

....

<tr>
..
</tr>

</table>

I have the following xpath to grab the download links

$xpath->query("//a[text()='Download']/@href");

What I am trying to figure out is the query to send to grab the Name of each of the downloads.

The page has no div id markups at all, just plain table, tr, td tags.

I have tried something like

$xpath->query("//preceding-sibling::a[text()='Download']");

Does anyone have any idea on this?

Upvotes: 0

Views: 1975

Answers (1)

C. M. Sperberg-McQueen
C. M. Sperberg-McQueen

Reputation: 25034

Close!

Given a particular context node (here, the href attribute for a download), you want to find the eldest sibling of the td containing the context node. So your relative path should first ascend to the td and then find the oldest sibling:

parent::a/parent::td/preceding-sibling::td[last()]

or more briefly (and without assuming that there are no elements like p or span intervening between the td and the a):

ancestor::td[1]/preceding-sibling::td[last()]

Some users find the reverse numbering of nodes on the preceding-sibling axis confusing, so it may feel simpler to say that what you really want is the first td child of the smallest containing tr:

ancestor::tr[1]/child::td[1]

If you need in a single pass to pick up all the download links and the textual label for them, then how you do it depends on the context in which you're using XPath. In XSLT, for example, you might write:

<xsl:apply-templates select="//a[text()='Download']/@href"/>

and then fetch the label in the appropriate template:

<xsl:template match="a/@href">
  <xsl:value-of select="string(ancestor::tr[1]/td[1])"/>
  : 
  <xsl:value-of select="."/>
</xsl:template> 

In other host languages, you will want to do something similar. The key problem is that you have to iterate over the nodes matching your expression for href, and then for each of those nodes you need to move back in the document to pick up the label. How you say "evaluate this second XPath expression based on the current node from the first XPath expression" will vary with your environment.

Upvotes: 1

Related Questions