Skip to main content
SeleniumDecoded

XPath Advanced Techniques

Master advanced XPath patterns for complex element location scenarios.

Selenium 3 & 4 Medium

This guide covers advanced XPath techniques for handling complex scenarios that simpler locators can’t address.

Boolean Operators

Combine conditions using and, or, and not():

Boolean Logic in XPath
Selenium 3 & 4 Stable
// AND - both conditions must be true
WebElement textInput = driver.findElement(
By.xpath("//input[@type='text' and @name='username']")
);
// OR - either condition
WebElement anyButton = driver.findElement(
By.xpath("//button[@type='submit' or @type='button']")
);
// NOT - exclude elements
WebElement enabledInput = driver.findElement(
By.xpath("//input[not(@disabled)]")
);
// Complex combination
WebElement specific = driver.findElement(By.xpath(
"//input[@type='text' and not(@readonly) and (contains(@class, 'form') or @name='search')]"
));
// Find visible elements (not hidden)
WebElement visible = driver.findElement(
By.xpath("//div[not(contains(@style, 'display: none'))]")
);
# AND - both conditions must be true
text_input = driver.find_element(
By.XPATH, "//input[@type='text' and @name='username']"
)
# OR - either condition
any_button = driver.find_element(
By.XPATH, "//button[@type='submit' or @type='button']"
)
# NOT - exclude elements
enabled_input = driver.find_element(
By.XPATH, "//input[not(@disabled)]"
)
# Complex combination
specific = driver.find_element(By.XPATH,
"//input[@type='text' and not(@readonly) and (contains(@class, 'form') or @name='search')]"
)
# Find visible elements (not hidden)
visible = driver.find_element(
By.XPATH, "//div[not(contains(@style, 'display: none'))]"
)
// AND - both conditions must be true
const textInput = await driver.findElement(
By.xpath("//input[@type='text' and @name='username']")
);
// OR - either condition
const anyButton = await driver.findElement(
By.xpath("//button[@type='submit' or @type='button']")
);
// NOT - exclude elements
const enabledInput = await driver.findElement(
By.xpath("//input[not(@disabled)]")
);
// Complex combination
const specific = await driver.findElement(By.xpath(
"//input[@type='text' and not(@readonly) and (contains(@class, 'form') or @name='search')]"
));
// Find visible elements (not hidden)
const visible = await driver.findElement(
By.xpath("//div[not(contains(@style, 'display: none'))]")
);
// AND - both conditions must be true
IWebElement textInput = driver.FindElement(
By.XPath("//input[@type='text' and @name='username']")
);
// OR - either condition
IWebElement anyButton = driver.FindElement(
By.XPath("//button[@type='submit' or @type='button']")
);
// NOT - exclude elements
IWebElement enabledInput = driver.FindElement(
By.XPath("//input[not(@disabled)]")
);
// Complex combination
IWebElement specific = driver.FindElement(By.XPath(
"//input[@type='text' and not(@readonly) and (contains(@class, 'form') or @name='search')]"
));
// Find visible elements (not hidden)
IWebElement visible = driver.FindElement(
By.XPath("//div[not(contains(@style, 'display: none'))]")
);

Axes Navigation

XPath axes provide powerful navigation through the DOM:

XPath Axes
Selenium 3 & 4 Medium
// ancestor - all ancestors up to root
WebElement form = driver.findElement(
By.xpath("//input[@id='email']/ancestor::form")
);
// ancestor-or-self - includes current element
WebElement formOrSelf = driver.findElement(
By.xpath("//form[@id='login']/ancestor-or-self::*[@class='container']")
);
// descendant - all children at any depth
List<WebElement> allInputs = driver.findElements(
By.xpath("//form[@id='login']/descendant::input")
);
// following - everything after in document order
WebElement nextSection = driver.findElement(
By.xpath("//h2[text()='Section 1']/following::h2")
);
// following-sibling - siblings after current
List<WebElement> laterItems = driver.findElements(
By.xpath("//li[@class='active']/following-sibling::li")
);
// preceding - everything before in document order
WebElement prevSection = driver.findElement(
By.xpath("//h2[text()='Section 2']/preceding::h2")
);
// preceding-sibling - siblings before current
WebElement labelBefore = driver.findElement(
By.xpath("//input[@id='email']/preceding-sibling::label")
);
# ancestor - all ancestors up to root
form = driver.find_element(
By.XPATH, "//input[@id='email']/ancestor::form"
)
# ancestor-or-self - includes current element
form_or_self = driver.find_element(
By.XPATH, "//form[@id='login']/ancestor-or-self::*[@class='container']"
)
# descendant - all children at any depth
all_inputs = driver.find_elements(
By.XPATH, "//form[@id='login']/descendant::input"
)
# following - everything after in document order
next_section = driver.find_element(
By.XPATH, "//h2[text()='Section 1']/following::h2"
)
# following-sibling - siblings after current
later_items = driver.find_elements(
By.XPATH, "//li[@class='active']/following-sibling::li"
)
# preceding - everything before in document order
prev_section = driver.find_element(
By.XPATH, "//h2[text()='Section 2']/preceding::h2"
)
# preceding-sibling - siblings before current
label_before = driver.find_element(
By.XPATH, "//input[@id='email']/preceding-sibling::label"
)
// ancestor - all ancestors up to root
const form = await driver.findElement(
By.xpath("//input[@id='email']/ancestor::form")
);
// ancestor-or-self - includes current element
const formOrSelf = await driver.findElement(
By.xpath("//form[@id='login']/ancestor-or-self::*[@class='container']")
);
// descendant - all children at any depth
const allInputs = await driver.findElements(
By.xpath("//form[@id='login']/descendant::input")
);
// following - everything after in document order
const nextSection = await driver.findElement(
By.xpath("//h2[text()='Section 1']/following::h2")
);
// following-sibling - siblings after current
const laterItems = await driver.findElements(
By.xpath("//li[@class='active']/following-sibling::li")
);
// preceding - everything before in document order
const prevSection = await driver.findElement(
By.xpath("//h2[text()='Section 2']/preceding::h2")
);
// preceding-sibling - siblings before current
const labelBefore = await driver.findElement(
By.xpath("//input[@id='email']/preceding-sibling::label")
);
// ancestor - all ancestors up to root
IWebElement form = driver.FindElement(
By.XPath("//input[@id='email']/ancestor::form")
);
// ancestor-or-self - includes current element
IWebElement formOrSelf = driver.FindElement(
By.XPath("//form[@id='login']/ancestor-or-self::*[@class='container']")
);
// descendant - all children at any depth
IList<IWebElement> allInputs = driver.FindElements(
By.XPath("//form[@id='login']/descendant::input")
);
// following - everything after in document order
IWebElement nextSection = driver.FindElement(
By.XPath("//h2[text()='Section 1']/following::h2")
);
// following-sibling - siblings after current
IList<IWebElement> laterItems = driver.FindElements(
By.XPath("//li[@class='active']/following-sibling::li")
);
// preceding - everything before in document order
IWebElement prevSection = driver.FindElement(
By.XPath("//h2[text()='Section 2']/preceding::h2")
);
// preceding-sibling - siblings before current
IWebElement labelBefore = driver.FindElement(
By.XPath("//input[@id='email']/preceding-sibling::label")
);

Working with Tables

XPath is excellent for table navigation:

Table Navigation with XPath
Selenium 3 & 4 Medium
// Find cell by row and column index
WebElement cell = driver.findElement(
By.xpath("//table[@id='data']//tr[3]/td[2]")
);
// Find row containing specific text
WebElement row = driver.findElement(
By.xpath("//table[@id='data']//tr[td[contains(text(), 'John')]]")
);
// Get all cells in a column (e.g., 3rd column)
List<WebElement> column = driver.findElements(
By.xpath("//table[@id='data']//tr/td[3]")
);
// Find cell relative to header
// First, find which column index "Price" is in, then get data
WebElement priceHeader = driver.findElement(
By.xpath("//table[@id='data']//th[text()='Price']")
);
// Count preceding siblings + 1 to get column position
int colIndex = driver.findElements(
By.xpath("//table[@id='data']//th[text()='Price']/preceding-sibling::th")
).size() + 1;
WebElement firstPrice = driver.findElement(
By.xpath("//table[@id='data']//tbody/tr[1]/td[" + colIndex + "]")
);
// Find action button in row with specific value
WebElement editBtn = driver.findElement(By.xpath(
"//table//tr[td[text()='Product A']]//button[text()='Edit']"
));
# Find cell by row and column index
cell = driver.find_element(
By.XPATH, "//table[@id='data']//tr[3]/td[2]"
)
# Find row containing specific text
row = driver.find_element(
By.XPATH, "//table[@id='data']//tr[td[contains(text(), 'John')]]"
)
# Get all cells in a column (e.g., 3rd column)
column = driver.find_elements(
By.XPATH, "//table[@id='data']//tr/td[3]"
)
# Find cell relative to header
price_header = driver.find_element(
By.XPATH, "//table[@id='data']//th[text()='Price']"
)
# Count preceding siblings + 1 to get column position
col_index = len(driver.find_elements(
By.XPATH, "//table[@id='data']//th[text()='Price']/preceding-sibling::th"
)) + 1
first_price = driver.find_element(
By.XPATH, f"//table[@id='data']//tbody/tr[1]/td[{col_index}]"
)
# Find action button in row with specific value
edit_btn = driver.find_element(By.XPATH,
"//table//tr[td[text()='Product A']]//button[text()='Edit']"
)
// Find cell by row and column index
const cell = await driver.findElement(
By.xpath("//table[@id='data']//tr[3]/td[2]")
);
// Find row containing specific text
const row = await driver.findElement(
By.xpath("//table[@id='data']//tr[td[contains(text(), 'John')]]")
);
// Get all cells in a column (e.g., 3rd column)
const column = await driver.findElements(
By.xpath("//table[@id='data']//tr/td[3]")
);
// Find cell relative to header
const priceHeader = await driver.findElement(
By.xpath("//table[@id='data']//th[text()='Price']")
);
const precedingSiblings = await driver.findElements(
By.xpath("//table[@id='data']//th[text()='Price']/preceding-sibling::th")
);
const colIndex = precedingSiblings.length + 1;
const firstPrice = await driver.findElement(
By.xpath(`//table[@id='data']//tbody/tr[1]/td[${colIndex}]`)
);
// Find action button in row with specific value
const editBtn = await driver.findElement(By.xpath(
"//table//tr[td[text()='Product A']]//button[text()='Edit']"
));
// Find cell by row and column index
IWebElement cell = driver.FindElement(
By.XPath("//table[@id='data']//tr[3]/td[2]")
);
// Find row containing specific text
IWebElement row = driver.FindElement(
By.XPath("//table[@id='data']//tr[td[contains(text(), 'John')]]")
);
// Get all cells in a column (e.g., 3rd column)
IList<IWebElement> column = driver.FindElements(
By.XPath("//table[@id='data']//tr/td[3]")
);
// Find cell relative to header
IWebElement priceHeader = driver.FindElement(
By.XPath("//table[@id='data']//th[text()='Price']")
);
int colIndex = driver.FindElements(
By.XPath("//table[@id='data']//th[text()='Price']/preceding-sibling::th")
).Count + 1;
IWebElement firstPrice = driver.FindElement(
By.XPath($"//table[@id='data']//tbody/tr[1]/td[{colIndex}]")
);
// Find action button in row with specific value
IWebElement editBtn = driver.FindElement(By.XPath(
"//table//tr[td[text()='Product A']]//button[text()='Edit']"
));

Dynamic Elements

Handle elements with dynamic or partial attributes:

Handling Dynamic Attributes
Selenium 3 & 4 Medium
// ID starts with static prefix
WebElement dynamicId = driver.findElement(
By.xpath("//*[starts-with(@id, 'user_')]")
);
// ID ends with pattern (no built-in ends-with in XPath 1.0)
WebElement endsWithId = driver.findElement(By.xpath(
"//*[substring(@id, string-length(@id) - 5) = '_field']"
));
// Class contains value
WebElement partialClass = driver.findElement(
By.xpath("//*[contains(@class, 'active')]")
);
// Multiple partial matches
WebElement complex = driver.findElement(By.xpath(
"//*[contains(@class, 'btn') and contains(@class, 'primary')]"
));
// Match any attribute value
WebElement anyAttr = driver.findElement(
By.xpath("//*[@*='submit-button']")
);
// Attribute exists (any value)
WebElement hasAttr = driver.findElement(
By.xpath("//input[@placeholder]")
);
# ID starts with static prefix
dynamic_id = driver.find_element(
By.XPATH, "//*[starts-with(@id, 'user_')]"
)
# ID ends with pattern (no built-in ends-with in XPath 1.0)
ends_with_id = driver.find_element(By.XPATH,
"//*[substring(@id, string-length(@id) - 5) = '_field']"
)
# Class contains value
partial_class = driver.find_element(
By.XPATH, "//*[contains(@class, 'active')]"
)
# Multiple partial matches
complex = driver.find_element(By.XPATH,
"//*[contains(@class, 'btn') and contains(@class, 'primary')]"
)
# Match any attribute value
any_attr = driver.find_element(
By.XPATH, "//*[@*='submit-button']"
)
# Attribute exists (any value)
has_attr = driver.find_element(
By.XPATH, "//input[@placeholder]"
)
// ID starts with static prefix
const dynamicId = await driver.findElement(
By.xpath("//*[starts-with(@id, 'user_')]")
);
// ID ends with pattern (no built-in ends-with in XPath 1.0)
const endsWithId = await driver.findElement(By.xpath(
"//*[substring(@id, string-length(@id) - 5) = '_field']"
));
// Class contains value
const partialClass = await driver.findElement(
By.xpath("//*[contains(@class, 'active')]")
);
// Multiple partial matches
const complex = await driver.findElement(By.xpath(
"//*[contains(@class, 'btn') and contains(@class, 'primary')]"
));
// Match any attribute value
const anyAttr = await driver.findElement(
By.xpath("//*[@*='submit-button']")
);
// Attribute exists (any value)
const hasAttr = await driver.findElement(
By.xpath("//input[@placeholder]")
);
// ID starts with static prefix
IWebElement dynamicId = driver.FindElement(
By.XPath("//*[starts-with(@id, 'user_')]")
);
// ID ends with pattern (no built-in ends-with in XPath 1.0)
IWebElement endsWithId = driver.FindElement(By.XPath(
"//*[substring(@id, string-length(@id) - 5) = '_field']"
));
// Class contains value
IWebElement partialClass = driver.FindElement(
By.XPath("//*[contains(@class, 'active')]")
);
// Multiple partial matches
IWebElement complex = driver.FindElement(By.XPath(
"//*[contains(@class, 'btn') and contains(@class, 'primary')]"
));
// Match any attribute value
IWebElement anyAttr = driver.FindElement(
By.XPath("//*[@*='submit-button']")
);
// Attribute exists (any value)
IWebElement hasAttr = driver.FindElement(
By.XPath("//input[@placeholder]")
);

Real-World Patterns

Common XPath patterns for typical scenarios:

Common XPath Patterns
Selenium 3 & 4 Medium
// Find input by its label text
WebElement inputByLabel = driver.findElement(
By.xpath("//label[text()='Email']/following-sibling::input")
);
// Alternative: label with 'for' attribute
WebElement inputByFor = driver.findElement(By.xpath(
"//input[@id=//label[text()='Email']/@for]"
));
// Find error message for specific field
WebElement errorMsg = driver.findElement(By.xpath(
"//input[@name='email']/following-sibling::span[@class='error']"
));
// Find dropdown option by visible text
WebElement option = driver.findElement(By.xpath(
"//select[@name='country']/option[text()='United States']"
));
// Find card/item by title and click its button
WebElement cardButton = driver.findElement(By.xpath(
"//div[contains(@class, 'card')][.//h3[text()='Premium Plan']]//button"
));
// Find nth matching element globally
WebElement thirdResult = driver.findElement(
By.xpath("(//div[@class='search-result'])[3]")
);
// Modal/dialog that's currently visible
WebElement modal = driver.findElement(By.xpath(
"//div[contains(@class, 'modal')][not(contains(@class, 'hidden'))]"
));
# Find input by its label text
input_by_label = driver.find_element(
By.XPATH, "//label[text()='Email']/following-sibling::input"
)
# Alternative: label with 'for' attribute
input_by_for = driver.find_element(By.XPATH,
"//input[@id=//label[text()='Email']/@for]"
)
# Find error message for specific field
error_msg = driver.find_element(By.XPATH,
"//input[@name='email']/following-sibling::span[@class='error']"
)
# Find dropdown option by visible text
option = driver.find_element(By.XPATH,
"//select[@name='country']/option[text()='United States']"
)
# Find card/item by title and click its button
card_button = driver.find_element(By.XPATH,
"//div[contains(@class, 'card')][.//h3[text()='Premium Plan']]//button"
)
# Find nth matching element globally
third_result = driver.find_element(
By.XPATH, "(//div[@class='search-result'])[3]"
)
# Modal/dialog that's currently visible
modal = driver.find_element(By.XPATH,
"//div[contains(@class, 'modal')][not(contains(@class, 'hidden'))]"
)
// Find input by its label text
const inputByLabel = await driver.findElement(
By.xpath("//label[text()='Email']/following-sibling::input")
);
// Alternative: label with 'for' attribute
const inputByFor = await driver.findElement(By.xpath(
"//input[@id=//label[text()='Email']/@for]"
));
// Find error message for specific field
const errorMsg = await driver.findElement(By.xpath(
"//input[@name='email']/following-sibling::span[@class='error']"
));
// Find dropdown option by visible text
const option = await driver.findElement(By.xpath(
"//select[@name='country']/option[text()='United States']"
));
// Find card/item by title and click its button
const cardButton = await driver.findElement(By.xpath(
"//div[contains(@class, 'card')][.//h3[text()='Premium Plan']]//button"
));
// Find nth matching element globally
const thirdResult = await driver.findElement(
By.xpath("(//div[@class='search-result'])[3]")
);
// Modal/dialog that's currently visible
const modal = await driver.findElement(By.xpath(
"//div[contains(@class, 'modal')][not(contains(@class, 'hidden'))]"
));
// Find input by its label text
IWebElement inputByLabel = driver.FindElement(
By.XPath("//label[text()='Email']/following-sibling::input")
);
// Alternative: label with 'for' attribute
IWebElement inputByFor = driver.FindElement(By.XPath(
"//input[@id=//label[text()='Email']/@for]"
));
// Find error message for specific field
IWebElement errorMsg = driver.FindElement(By.XPath(
"//input[@name='email']/following-sibling::span[@class='error']"
));
// Find dropdown option by visible text
IWebElement option = driver.FindElement(By.XPath(
"//select[@name='country']/option[text()='United States']"
));
// Find card/item by title and click its button
IWebElement cardButton = driver.FindElement(By.XPath(
"//div[contains(@class, 'card')][.//h3[text()='Premium Plan']]//button"
));
// Find nth matching element globally
IWebElement thirdResult = driver.FindElement(
By.XPath("(//div[@class='search-result'])[3]")
);
// Modal/dialog that's currently visible
IWebElement modal = driver.FindElement(By.XPath(
"//div[contains(@class, 'modal')][not(contains(@class, 'hidden'))]"
));

XPath Debugging Tips

  1. Test in browser DevTools - Use $x("your-xpath") in console
  2. Start simple - Build complex XPaths incrementally
  3. Check uniqueness - Verify your XPath returns only one element
  4. Use Chrome extensions - ChroPath, SelectorsHub help generate XPaths
  5. Practice interactively - Try the XPath tester at XPath Decoded to experiment with expressions

Performance Considerations

ApproachSpeedNotes
IDFastestNative browser optimization
CSS SelectorFastWell-optimized in browsers
Simple XPathGood//*[@id='x'] is fast
Complex XPathSlowerAxes and functions add overhead
Text-based XPathSlowestRequires text node scanning

Next Steps