How to select specified node within Xpath node sets by index with Selenium?

Viewed 186012

I'm writing a Selenium testcase. And here's the xpath expression I use to match all 'Modify' buttons within a data table.

//img[@title='Modify']

My question is, how can I visit the matched node sets by index? I've tried with

//img[@title='Modify'][i]

and

//img[@title='Modify' and position() = i]

But neither works.. I also tried with XPath checker(One firefox extension). There're totally 13 matches found, then I have totally no idea how am I gonna select one of them.. Or does XPath support specified selection of nodes which are not under same parent node?

6 Answers

There is no i in xpath is not entirely true. You can still use the count() to find the index.

Consider the following page

<html>

 <head>
  <title>HTML Sample table</title>
 </head>

 <style>
 table, td, th {
  border: 1px solid black;
  font-size: 15px;
  font-family: Trebuchet MS, sans-serif;
 }
 table {
  border-collapse: collapse;
  width: 100%;
 }

 th, td {
  text-align: left;
  padding: 8px;
 }

 tr:nth-child(even){background-color: #f2f2f2}

 th {
  background-color: #4CAF50;
  color: white;
 }
 </style>

 <body>
 <table>
  <thead>
   <tr>
    <th>Heading 1</th>
    <th>Heading 2</th>
    <th>Heading 3</th>
    <th>Heading 4</th>
    <th>Heading 5</th>
    <th>Heading 6</th>
   </tr>
  </thead>
  <tbody>
   <tr>
    <td>Data row 1 col 1</td>
    <td>Data row 1 col 2</td>
    <td>Data row 1 col 3</td>
    <td>Data row 1 col 4</td>
    <td>Data row 1 col 5</td>
    <td>Data row 1 col 6</td>
   </tr>
   <tr>
    <td>Data row 2 col 1</td>
    <td>Data row 2 col 2</td>
    <td>Data row 2 col 3</td>
    <td>Data row 2 col 4</td>
    <td>Data row 2 col 5</td>
    <td>Data row 2 col 6</td>
   </tr>
   <tr>
    <td>Data row 3 col 1</td>
    <td>Data row 3 col 2</td>
    <td>Data row 3 col 3</td>
    <td>Data row 3 col 4</td>
    <td>Data row 3 col 5</td>
    <td>Data row 3 col 6</td>
   </tr>
   <tr>
    <td>Data row 4 col 1</td>
    <td>Data row 4 col 2</td>
    <td>Data row 4 col 3</td>
    <td>Data row 4 col 4</td>
    <td>Data row 4 col 5</td>
    <td>Data row 4 col 6</td>
   </tr>
   <tr>
    <td>Data row 5 col 1</td>
    <td>Data row 5 col 2</td>
    <td>Data row 5 col 3</td>
    <td>Data row 5 col 4</td>
    <td>Data row 5 col 5</td>
    <td>Data row 5 col 6</td>
   </tr>
   <tr>
    <td><button>Modify</button></td>
    <td><button>Modify</button></td>
    <td><button>Modify</button></td>
    <td><button>Modify</button></td>
    <td><button>Modify</button></td>
    <td><button>Modify</button></td>
   </tr>
  </tbody>
 </table>

 </br>

 <table>
  <thead>
   <tr>
    <th>Heading 7</th>
    <th>Heading 8</th>
    <th>Heading 9</th>
    <th>Heading 10</th>
    <th>Heading 11</th>
    <th>Heading 12</th>
   </tr>
  </thead>
  <tbody>
   <tr>
    <td>Data row 1 col 1</td>
    <td>Data row 1 col 2</td>
    <td>Data row 1 col 3</td>
    <td>Data row 1 col 4</td>
    <td>Data row 1 col 5</td>
    <td>Data row 1 col 6</td>
   </tr>
   <tr>
    <td>Data row 2 col 1</td>
    <td>Data row 2 col 2</td>
    <td>Data row 2 col 3</td>
    <td>Data row 2 col 4</td>
    <td>Data row 2 col 5</td>
    <td>Data row 2 col 6</td>
   </tr>
   <tr>
    <td>Data row 3 col 1</td>
    <td>Data row 3 col 2</td>
    <td>Data row 3 col 3</td>
    <td>Data row 3 col 4</td>
    <td>Data row 3 col 5</td>
    <td>Data row 3 col 6</td>
   </tr>
   <tr>
    <td>Data row 4 col 1</td>
    <td>Data row 4 col 2</td>
    <td>Data row 4 col 3</td>
    <td>Data row 4 col 4</td>
    <td>Data row 4 col 5</td>
    <td>Data row 4 col 6</td>
   </tr>
   <tr>
    <td>Data row 5 col 1</td>
    <td>Data row 5 col 2</td>
    <td>Data row 5 col 3</td>
    <td>Data row 5 col 4</td>
    <td>Data row 5 col 5</td>
    <td>Data row 5 col 6</td>
   </tr>
   <tr>
    <td><button>Modify</button></td>
    <td><button>Modify</button></td>
    <td><button>Modify</button></td>
    <td><button>Modify</button></td>
    <td><button>Modify</button></td>
    <td><button>Modify</button></td>
   </tr>
  </tbody>
 </table>

 </body>
</html>

The page has 2 tables and has 6 columns each with unique column names and 6 rows with variable data. The last row has the Modify button in both the tables.

Assuming that the user has to select the 4th Modify button from the first table based on the heading

Use the xpath //th[.='Heading 4']/ancestor::thead/following-sibling::tbody/tr/td[count(//tr/th[.='Heading 4']/preceding-sibling::th)+1]/button

The count() operator comes in handy in situations like these.

Logic:

  1. Find the header for the Modify button using //th[.='Heading 4']
  2. Find the index of the header column using count(//tr/th[.='Heading 4']/preceding-sibling::th)+1

Note: Index starts at 0

  1. Get the rows for the corresponding header using //th[.='Heading 4']/ancestor::thead/following-sibling::tbody/tr/td[count(//tr/th[.='Heading 4']/preceding-sibling::th)+1]

  2. Get the Modify button from the extracted node list using //th[.='Heading 4']/ancestor::thead/following-sibling::tbody/tr/td[count(//tr/th[.='Heading 4']/preceding-sibling::th)+1]/button

Here is the solution for index variable

Let's say, you have found 5 elements with same locator and you would like to perform action on each element by providing index number (here, variable is used for index as "i")

for(int i=1; i<=5; i++)
{
    string xPathWithVariable = "(//div[@class='className'])" + "[" + i + "]";
    driver.FindElement(By.XPath(xPathWithVariable)).Click();
}

It takes XPath :

(//div[@class='className'])[1]
(//div[@class='className'])[2]
(//div[@class='className'])[3]
(//div[@class='className'])[4]
(//div[@class='className'])[5]
Related