40

I use a lot of XPath when locating elements in web pages using Selenium, and have moved away from using node1//node2 towards using node1/descendant::node2 more recently. What's the difference between the two methods? Is one more efficient than the other?

Example XML snippet to demonstrate:

<div id="books">
  <table>
    <tr><td class="title">Lord of the Rings</td><td class="author">JRR Tolkein</td></tr>
    <tr><td class="title">The Hitch-Hikers Guide to the Galaxy</td><td class="author">Douglas Adams</td></tr>
  </table>
</div>

So it'd be:

id('books')//td[@class='title']

or:

id('books')/descendant::td[@class='title']
Dave Hunt
  • 8,191
  • 4
  • 38
  • 39
  • Possible duplicate of [Differences between // and /descendant in XPath selecting multiple children](https://stackoverflow.com/questions/33918010/differences-between-and-descendant-in-xpath-selecting-multiple-children) – Robert Columbia Jul 31 '19 at 15:49

5 Answers5

37

see http://www.w3.org/TR/xpath#path-abbrev

// is just an abbreviation for the descendant:: axis

Edit

To quote:

//para is short for /descendant-or-self::node()/child::para

That is, it refers to all para which are a child of the context node or any node descended from the context node. As far as I can tell that translates into any descendant para of the context node.

Jonathan Fingland
  • 56,385
  • 11
  • 85
  • 79
  • 1
    I should have scoured the TR before asking. For others http://www.w3.org/TR/xpath#path-abbrev is the relevant section. It would appear though that // is short for descendant-or-self so not exactly the same. Also, the reason I've stayed away from using // is that //node[1] is not the same as /descendant::node[1] – Dave Hunt Oct 08 '09 at 13:36
  • 1
    I've expanded on that point above. // is not descendant-or-self, it is descendant-or-self/child.... which looks an awful lot like descendant to me. – Jonathan Fingland Oct 08 '09 at 13:38
  • Please see Motty's answer, which highlights the actual important differences, i.e. `descendant-or-self::node()/child::para` is *not* the same as `descendant::para`. Can get you into trouble if you assume they are the same. For a more detailed answer see [this post](http://stackoverflow.com/questions/33918010/differences-between-and-descendant-in-xpath-selecting-multiple-children#33920245). – Thomas W Nov 29 '15 at 07:37
14

There's a difference in the context group. //para[1] is short for /descendant-or-self::node()/child::para[1], which returns every para that is the first child of its parent. /descendant::para[1] returns only the first para in the entire subtree.

Zakaria Acharki
  • 66,747
  • 15
  • 75
  • 101
Motty
  • 141
  • 1
  • 3
  • Also (//para)[1] should also work but doesn't in Selenium 1 so you have to use /descendant::para[1]. Anyone know how the context group is applied differently between//node[1] and /descendant::node[1]? – Carl Pritchett Apr 19 '13 at 04:06
8

In your case

 id('books')//td[@class='title']

and:

 id('books')/descendant::td[@class='title']

return the same result.

But in fact, like it was already stated before, id('books')//td[@class='title'] means id('books')/descendant-or-self::node()/td[@class='title'] which is different from id('books')/descendant::td[@class='title'] in concept.

See the following note:

NOTE: The location path //para[1] does not mean the same as the location path /descendant::para[1]. The latter selects the first descendant para element; the former selects all descendant para elements that are the first para children of their parents.

this note was taken from http://www.w3.org/TR/xpath#path-abbrev

Zakaria Acharki
  • 66,747
  • 15
  • 75
  • 101
JBakouny
  • 151
  • 2
  • 2
2

Other than terseness, I'm not aware of any difference.

Allain Lalonde
  • 91,574
  • 70
  • 187
  • 238
1

I realize this answer is more of a functional correctness, but in case it helps anyone, I stumbled across a key difference today.

When selecting elements based on some property of a descendant (like attribute). For example:

...
<foo id="ID1">
    <bar>
        <baz at2="c" />
    </bar>
</foo>
<foo2 id="ID2">
    <bar>
        <baz at2="d" />
    </bar>
</foo2>
...

To select the id attribute of all elements who have a descendant with an attribute at2=c:

This xpath will select foo and foo2:

//*[//*[@at2="c"]]/@id

while this xpath will only select foo:

//*[descendant::*[@at2="c"]]/@id
codesniffer
  • 1,033
  • 9
  • 22