1

I was debugging someone else's Selenium code. They had an xpath something like td['6']. This was failing. I used intuition and changed it to td[6] which fixed it. However, td['6'] did not give an error as I thought it would. It located an element, but a completely different one than without the quotes.

So it got me to thinking, what does putting the number in quotes, like td['6'] actually mean?

Grant Foster
  • 722
  • 2
  • 11
  • 21
Tony
  • 1,127
  • 1
  • 18
  • 29

2 Answers2

3

The XPath 1.0 Specification states that ’td’ is the axis (e.g. children) and node-test (tag name TD) an everything inside the square brackets is a predicate-expression which is either evaluated as boolean (true or false) or in special-case of a number evaluated as positional filter of the node-set:

if the result is not a number, then the result will be converted as if by a call to the boolean function. Thus a location path para[3] is equivalent to para[position()=3].

Explained by Example

Following 3 XPath predicate cases are explained by evaluating on input from this sample table (w3schools):

+-------------------------------+-------------------+---------+
|           Company             |     Contact       | Country |
+-------------------------------+-------------------+---------+
| Alfreds Futterkiste           | Maria Anders      | Germany |
| Centro comercial Moctezuma    | Francisco Chang   | Mexico  |
| Ernst Handel                  | Roland Mendel     | Austria |
| Island Trading                | Helen Bennett     | UK      |
| Laughing Bacchus Winecellars  | Yoshi Tannamuri   | Canada  |
| Magazzini Alimentari Riuniti  | Giovanni Rovelli  | Italy   |
+-------------------------------+-------------------+---------+

case 1: number as predicate

td[6] selects the 6th child table-data element since number 6 is evaluated as shorthand for predicate position()=6.

Example: select each cell of the the third column

XPath: //td[3]

Output:

  <td>Germany</td>  
  <td>Mexico</td>  
  <td>Austria</td>  
  <td>UK</td>  
  <td>Canada</td>  
  <td>Italy</td> 

Try online demo

case 2: quoted string as predicate

td['6'] selects every child table-data element since the string '6' is not-empty or has non-zero length and thus evaluates to true (see boolean-conversion). So node-set of TD-elements is not further filtered (because predicate is always true).

Example: select each cell (because the string-predicate is always true)

XPath: //td['3']

Output:

  <td>Alfreds Futterkiste</td>  
  <td>Maria Anders</td>  
  <td>Germany</td>  
  <td>Centro comercial Moctezuma</td>  
  <td>Francisco Chang</td>  
  <td>Mexico</td>  
  <td>Ernst Handel</td>  
  <td>Roland Mendel</td>  
  <td>Austria</td>  
  <td>Island Trading</td>  
  <td>Helen Bennett</td>  
  <td>UK</td>  
  <td>Laughing Bacchus Winecellars</td>  
  <td>Yoshi Tannamuri</td>  
  <td>Canada</td>  
  <td>Magazzini Alimentari Riuniti</td>  
  <td>Giovanni Rovelli</td>  
  <td>Italy</td> 

Try online demo

case 3: conditional predicate testing the elements

This is the real benefit of predicate expression and allows you to test on the elements, for example find all merged table-cells with colspan attribute:

td[@colspan]

See this sophisticated use-case: Xpath expression with multiple predicates

Example: select all cells where contents starts with 'A'

XPath: //tr/td[starts-with(., 'A')]

Output:

  <td>Alfreds Futterkiste</td>  
  <td>Austria</td>  

Try online demo

hc_dev
  • 8,389
  • 1
  • 26
  • 38
  • This confuses me. What else could it be? "a number is true if and only if it is neither positive or negative zero nor NaN" – Tony Feb 20 '19 at 13:04
  • @Tony the "_positive or negative zero_" is mathematical language for 0 [as discussed here](https://math.stackexchange.com/questions/997294/how-is-it-true-that-zero-is-neither-a-positive-number-nor-a-negative-number). The other "_NaN_" means _not-a-number_. [MSDN explaines better in this question similar to yours](https://stackoverflow.com/a/346227). Still or even more confused now? – hc_dev Feb 20 '19 at 13:24
  • I thought they just forgot a comma. It is not positive, or negative, or zero or NAN. Better "positivie zero, negative zero, or NaN" – Tony Feb 20 '19 at 16:46
  • Is there any time it makes sense to put a predicate inside []? I haven't found any way to use variables in xpaths? Or do you do something like td['td[5]'] and it is true only if td has a 5th element? – Tony Feb 20 '19 at 17:32
  • @Tony Glad you asked :) The square brackets `[]` __is the predicate__, everything inside them the (predicate-) _expression_. To check if something exists use [XPath's _boolean-function_ with your _node-selector_ as argument](https://stackoverflow.com/questions/5689966/how-to-check-if-an-element-exists-in-the-xml-using-xpath). So `boolean(td[5])` checks if 5th table-data cell exists. – hc_dev Feb 22 '19 at 18:35
  • @Tony you asked when `it makes sense to put a predicate inside []`. Updated my answer with _case 3_ – hc_dev Dec 29 '20 at 19:47
0

td[predicate] means:

return the first td node for which predicate is true.

Each non-empty string returns true, so td['6'] will select first td node found in DOM.

td[6] is the shorthand for td[position()=6] expression which means:

return td which is the sixth child of td type."

Kzryzstof
  • 7,688
  • 10
  • 61
  • 108
JaSON
  • 4,843
  • 2
  • 8
  • 15
  • thanks. I understood td[6]. Would be bad of me to ask what that meant ;-). So it definitely was an error the guy made. Surprised they passed the test. Actually there were testing whether it was eidtable, which I am guessing td['6'] was not editable so it looked as though the test passed. – Tony Feb 19 '19 at 21:09
  • 1
    Almost correct answer. Actually `td['6']` will select *every* `td` child, not just the first. In some contexts when you select several nodes, all but the first are ignored, but in general this isn't the case, for example `count(td['6'])` will return the same result as `count(child::td)`. – Michael Kay Feb 20 '19 at 00:45
  • @MichaelKay , yep, but as OP has mentioned Selenium I assumed that OP is using `findElement` method that can select *single element only (the first found)* – JaSON Feb 28 '19 at 20:21
  • @JaSON Sounds like you, like the OP, are someone who's strong on intuition. I'm not. – Michael Kay Feb 28 '19 at 20:29