XPath Axes
XPath Axes define the relationship between nodes in the Document Object Model (DOM). They allow you to navigate to different parts of the document relative to the current node, enabling precise and flexible element selection. Understanding XPath axes is crucial for crafting effective XPath expressions, especially in complex and dynamic web applications.
Common XPath Axes:
-
child
: Selects all child elements of the current node. -
parent
: Selects the parent of the current node. -
ancestor
: Selects all ancestors (parents, grandparents, etc.) of the current node. -
descendant
: Selects all descendants (children, grandchildren, etc.) of the current node. -
following-sibling
: Selects all siblings that appear after the current node. -
preceding-sibling
: Selects all siblings that appear before the current node. -
self
: Selects the current node itself. -
ancestor-or-self
: Selects all ancestors and the current node. -
descendant-or-self
: Selects all descendants and the current node. -
following
: Selects everything in the document after the closing tag of the current node. -
preceding
: Selects everything in the document before the opening tag of the current node.
Let’s delve into each of these axes with detailed explanations, illustrative HTML examples, and corresponding Selenium WebDriver code snippets using Java.
1. child
Axis
Description:
The child
axis selects all direct child elements of the
current node. It is often used to traverse from a parent element to
its immediate children.
HTML Example:
<div id="parentDiv">
<input type="text" id="childInput1" name="input1">
<button id="childButton1">Submit</button>
</div>
XPath Expression:
//div[@id='parentDiv']/child::input
Explanation:
This XPath selects the <input>
element that is a
direct child of the <div>
with
id='parentDiv'
.
Selenium Example:
// Locate the parent div
WebElement parentDiv = driver.findElement(By.id("parentDiv"));
// Using XPath with child axis to find the input element
WebElement childInput = parentDiv.findElement(By.xpath("./child::input[@id='childInput1']"));
childInput.sendKeys("Test Input");
2. parent
Axis
Description:
The parent
axis selects the immediate parent of the
current node. It is useful when you have a reference to a child
element and need to interact with its parent.
HTML Example:
<form id="loginForm">
<label for="username">Username:</label>
<input type="text" id="username" name="user">
</form>
XPath Expression:
//input[@id='username']/parent::form
Explanation:
This XPath selects the <form>
element that is the
parent of the <input>
with
id='username'
.
Selenium Example:
// Locate the input field
WebElement usernameInput = driver.findElement(By.id("username"));
// Using XPath with parent axis to find the form element
WebElement parentForm = usernameInput.findElement(By.xpath("./parent::form"));
System.out.println("Form ID: " + parentForm.getAttribute("id"));
3. ancestor
Axis
Description:
The ancestor
axis selects all ancestor elements (parents,
grandparents, etc.) of the current node. It is beneficial when you
need to traverse up multiple levels in the DOM hierarchy.
HTML Example:
<html>
<body>
<div class="container">
<form id="signupForm">
<fieldset>
<legend>Sign Up</legend>
<input type="email" id="email" name="email">
</fieldset>
</form>
</div>
</body>
</html>
XPath Expression:
//input[@id='email']/ancestor::div[@class='container']
Explanation:
This XPath selects the <div>
with
class='container'
that is an ancestor of the
<input>
with id='email'
.
Selenium Example:
// Locate the email input field
WebElement emailInput = driver.findElement(By.id("email"));
// Using XPath with ancestor axis to find the container div
WebElement containerDiv = emailInput.findElement(By.xpath("./ancestor::div[@class='container']"));
System.out.println("Container Class: " + containerDiv.getAttribute("class"));
4. descendant
Axis
Description:
The descendant
axis selects all descendant elements
(children, grandchildren, etc.) of the current node. It is useful for
searching through nested elements within a parent.
HTML Example:
<div class="menu">
<ul>
<li><a href="/home">Home</a></li>
<li>
<a href="/products">Products</a>
<ul>
<li><a href="/products/software">Software</a></li>
<li><a href="/products/hardware">Hardware</a></li>
</ul>
</li>
<li><a href="/contact">Contact</a></li>
</ul>
</div>
XPath Expression:
//div[@class='menu']/descendant::a[text()='Software']
Explanation:
This XPath selects the <a>
element with the text
‘Software’ that is a descendant of the <div>
with
class='menu'
.
Selenium Example:
// Locate the menu div
WebElement menuDiv = driver.findElement(By.className("menu"));
// Using XPath with descendant axis to find the 'Software' link
WebElement softwareLink = menuDiv.findElement(By.xpath(".//descendant::a[text()='Software']"));
softwareLink.click();
5. following-sibling
Axis
Description:
The following-sibling
axis selects all siblings after the
current node. This is particularly useful when you want to interact
with elements that are adjacent or follow a known element.
HTML Example:
<label for="email">Email:</label>
<input type="email" id="email" name="email">
<button id="submitBtn">Submit</button>
XPath Expression:
//label[@for='email']/following-sibling::input
Explanation:
This XPath selects the <input>
element that is a
following sibling of the <label>
with
for='email'
.
Selenium Example:
// Locate the label
WebElement emailLabel = driver.findElement(By.xpath("//label[@for='email']"));
// Using XPath with following-sibling axis to find the input field
WebElement emailInput = emailLabel.findElement(By.xpath("./following-sibling::input[@id='email']"));
emailInput.sendKeys("user@example.com");
6. preceding-sibling
Axis
Description:
The preceding-sibling
axis selects all siblings before
the current node. It is useful when you need to interact with elements
that come before a known element.
HTML Example:
<button id="cancelBtn">Cancel</button>
<button id="submitBtn">Submit</button>
XPath Expression:
//button[@id='submitBtn']/preceding-sibling::button[@id='cancelBtn']
Explanation:
This XPath selects the <button>
with
id='cancelBtn'
that precedes the
<button>
with id='submitBtn'
.
Selenium Example:
// Locate the submit button
WebElement submitButton = driver.findElement(By.id("submitBtn"));
// Using XPath with preceding-sibling axis to find the cancel button
WebElement cancelButton = submitButton.findElement(By.xpath("./preceding-sibling::button[@id='cancelBtn']"));
cancelButton.click();
7. self
Axis
Description:
The self
axis selects the current node itself. While not
frequently used, it can be beneficial in specific scenarios where you
want to apply predicates to the current node.
HTML Example:
<div class="notification">
<p>Welcome to the site!</p>
</div>
XPath Expression:
//div[@class='notification']/self::div[@class='notification']
Explanation:
This XPath selects the <div>
element with
class='notification'
itself.
Selenium Example:
// Locate the notification div using self axis
WebElement notificationDiv = driver.findElement(By.xpath("//div[@class='notification']/self::div[@class='notification']"));
System.out.println("Notification Text: " + notificationDiv.getText());
8. ancestor-or-self
Axis
Description:
The ancestor-or-self
axis selects all ancestors of the
current node including the node itself. This is
useful when you want to include the current node in your selection
criteria.
HTML Example:
<div class="grandparent">
<div class="parent">
<div class="child" id="targetChild">
<span>Child Element</span>
</div>
</div>
</div>
XPath Expression:
//span[text()='Child Element']/ancestor-or-self::div[@class='parent']
Explanation:
This XPath selects the <div>
with
class='parent'
that is an ancestor of the
<span>
with the text ‘Child Element’, including
itself if it matches.
Selenium Example:
// Locate the span element
WebElement childSpan = driver.findElement(By.xpath("//span[text()='Child Element']"));
// Using XPath with ancestor-or-self axis to find the parent div
WebElement parentDiv = childSpan.findElement(By.xpath("./ancestor-or-self::div[@class='parent']"));
System.out.println("Parent Div Class: " + parentDiv.getAttribute("class"));
9. descendant-or-self
Axis
Description:
The descendant-or-self
axis selects all descendants of
the current node including the node itself. This is
particularly useful when you want to include the current node in your
search for specific descendant elements.
HTML Example:
<section class="content">
<article id="article1">
<h2>Article Title</h2>
<p>Article content goes here.</p>
</article>
<article id="article2">
<h2>Another Article</h2>
<p>More content here.</p>
</article>
</section>
XPath Expression:
//section[@class='content']/descendant-or-self::article[@id='article2']/h2
Explanation:
This XPath selects the <h2>
element within the
<article>
with id='article2'
, which is
a descendant of the <section>
with
class='content'
, including the section itself in the
search.
Selenium Example:
// Locate the content section
WebElement contentSection = driver.findElement(By.className("content"));
// Using XPath with descendant-or-self axis to find the second article's title
WebElement articleTitle = contentSection.findElement(By.xpath(".//descendant-or-self::article[@id='article2']/h2"));
System.out.println("Article Title: " + articleTitle.getText());
10. following
Axis
Description:
The following
axis selects all nodes in the document that
come after the closing tag of the current node, excluding any
descendants. This can be useful for locating elements that appear
later in the document.
HTML Example:
<div id="startDiv">
<p>Start of the section.</p>
</div>
<div id="endDiv">
<p>End of the section.</p>
</div>
XPath Expression:
//div[@id='startDiv']/following::div[@id='endDiv']
Explanation:
This XPath selects the <div>
with
id='endDiv'
that follows the
<div>
with id='startDiv'
in the
document order.
Selenium Example:
// Locate the start div
WebElement startDiv = driver.findElement(By.id("startDiv"));
// Using XPath with following axis to find the end div
WebElement endDiv = startDiv.findElement(By.xpath("./following::div[@id='endDiv']"));
System.out.println("End Div Text: " + endDiv.getText());
11. preceding
Axis
Description:
The preceding
axis selects all nodes in the document that
come before the opening tag of the current node, excluding any
ancestors. This is useful for locating elements that appear earlier in
the document.
HTML Example:
<div id="header">
<h1>Website Header</h1>
</div>
<div id="mainContent">
<p>Main content goes here.</p>
</div>
XPath Expression:
//div[@id='mainContent']/preceding::div[@id='header']
Explanation:
This XPath selects the <div>
with
id='header'
that precedes the
<div>
with id='mainContent'
in the
document order.
Selenium Example:
// Locate the main content div
WebElement mainContentDiv = driver.findElement(By.id("mainContent"));
// Using XPath with preceding axis to find the header div
WebElement headerDiv = mainContentDiv.findElement(By.xpath("./preceding::div[@id='header']"));
System.out.println("Header Text: " + headerDiv.getText());
Complete Example with HTML and Selenium
To consolidate the understanding of XPath axes, let’s consider a complete example that includes an HTML structure and corresponding Selenium WebDriver code to interact with various elements using different axes.
HTML Example:
<!DOCTYPE html>
<html>
<head>
<title>XPath Axes Example</title>
</head>
<body>
<div class="container">
<header id="mainHeader">
<h1>Welcome to XPath Tutorial</h1>
</header>
<section class="content">
<article id="article1">
<h2>Article One</h2>
<p>This is the first article.</p>
<a href="/read-more">Read More</a>
</article>
<article id="article2">
<h2>Article Two</h2>
<p>This is the second article.</p>
<a href="/read-more">Read More</a>
</article>
</section>
<footer id="mainFooter">
<p>Footer Information</p>
</footer>
</div>
</body>
</html>
Selenium WebDriver Code (Java):
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
public class XPathAxesExample {
public static void main(String[] args) {
// Set the path to the ChromeDriver executable
System.setProperty("webdriver.chrome.driver", "path/to/chromedriver");
// Initialize ChromeDriver
WebDriver driver = new ChromeDriver();
try {
// Navigate to the example HTML page
driver.get("file:///path/to/your/example.html");
// 1. Using child axis to find the header
WebElement header = driver.findElement(By.xpath("//div[@class='container']/child::header[@id='mainHeader']"));
System.out.println("Header Text: " + header.findElement(By.tagName("h1")).getText());
// 2. Using descendant axis to find the second article's paragraph
WebElement article2Paragraph = driver.findElement(By.xpath("//article[@id='article2']/descendant::p"));
System.out.println("Article 2 Paragraph: " + article2Paragraph.getText());
// 3. Using ancestor axis to find the container div from the footer
WebElement footer = driver.findElement(By.xpath("//footer[@id='mainFooter']"));
WebElement containerDiv = footer.findElement(By.xpath("./ancestor::div[@class='container']"));
System.out.println("Container Div Class: " + containerDiv.getAttribute("class"));
// 4. Using following-sibling axis to find the second article's 'Read More' link
WebElement readMoreLink = driver.findElement(By.xpath("//article[@id='article1']/following-sibling::article[@id='article2']/descendant::a[text()='Read More']"));
System.out.println("Read More Link Href: " + readMoreLink.getAttribute("href"));
// 5. Using preceding-sibling axis to find the first article from the second article
WebElement firstArticle = driver.findElement(By.xpath("//article[@id='article2']/preceding-sibling::article[@id='article1']"));
System.out.println("First Article Title: " + firstArticle.findElement(By.tagName("h2")).getText());
// 6. Using self axis to confirm the footer's id
WebElement selfFooter = driver.findElement(By.xpath("//footer[@id='mainFooter']/self::footer"));
System.out.println("Self Footer ID: " + selfFooter.getAttribute("id"));
// 7. Using ancestor-or-self axis to find the main header from the h1 element
WebElement h1Element = driver.findElement(By.xpath("//h1[text()='Welcome to XPath Tutorial']"));
WebElement ancestorOrSelfHeader = h1Element.findElement(By.xpath("./ancestor-or-self::header[@id='mainHeader']"));
System.out.println("Ancestor-Or-Self Header ID: " + ancestorOrSelfHeader.getAttribute("id"));
// 8. Using descendant-or-self axis to find all paragraphs within the content section
WebElement contentSection = driver.findElement(By.xpath("//section[@class='content']"));
java.util.List<WebElement> paragraphs = contentSection.findElements(By.xpath(".//descendant-or-self::p"));
for(WebElement para : paragraphs) {
System.out.println("Paragraph Text: " + para.getText());
}
// 9. Using following axis to find the footer after the content section
WebElement footerAfterContent = driver.findElement(By.xpath("//section[@class='content']/following::footer[@id='mainFooter']"));
System.out.println("Footer Text: " + footerAfterContent.findElement(By.tagName("p")).getText());
// 10. Using preceding axis to find the header before the content section
WebElement headerBeforeContent = driver.findElement(By.xpath("//section[@class='content']/preceding::header[@id='mainHeader']"));
System.out.println("Header Before Content: " + headerBeforeContent.findElement(By.tagName("h1")).getText());
} catch(Exception e) {
e.printStackTrace();
} finally {
// Close the browser after the operations
driver.quit();
}
}
}
Explanation of the Selenium Code:
-
Child Axis (
child
):-
Objective: Locate the
<header>
element that is a direct child of the<div>
withclass='container'
. -
Code:
WebElement header = driver.findElement(By.xpath("//div[@class='container']/child::header[@id='mainHeader']"));
-
Action: Print the text within the
<h1>
tag inside the header.
-
Objective: Locate the
-
Descendant Axis (
descendant
):-
Objective: Find the
<p>
tag within the<article>
withid='article2'
. -
Code:
WebElement article2Paragraph = driver.findElement(By.xpath("//article[@id='article2']/descendant::p"));
- Action: Print the paragraph text.
-
Objective: Find the
-
Ancestor Axis (
ancestor
):-
Objective: From the
<footer>
element, find its ancestor<div>
withclass='container'
. -
Code:
WebElement containerDiv = footer.findElement(By.xpath("./ancestor::div[@class='container']"));
- Action: Print the class attribute of the container div.
-
Objective: From the
-
Following-Sibling Axis (
following-sibling
):-
Objective: From the first
<article>
, find the ‘Read More’ link in the second<article>
. -
Code:
WebElement readMoreLink = driver.findElement(By.xpath("//article[@id='article1']/following-sibling::article[@id='article2']/descendant::a[text()='Read More']"));
-
Action: Print the
href
attribute of the ‘Read More’ link.
-
Objective: From the first
-
Preceding-Sibling Axis (
preceding-sibling
):-
Objective: From the second
<article>
, find the first<article>
. -
Code:
WebElement firstArticle = driver.findElement(By.xpath("//article[@id='article2']/preceding-sibling::article[@id='article1']"));
- Action: Print the title of the first article.
-
Objective: From the second
-
Self Axis (
self
):-
Objective: Confirm that the
<footer>
element is itself a<footer>
tag. -
Code:
WebElement selfFooter = driver.findElement(By.xpath("//footer[@id='mainFooter']/self::footer"));
-
Action: Print the
id
of the footer.
-
Objective: Confirm that the
-
Ancestor-Or-Self Axis (
ancestor-or-self
):-
Objective: From the
<h1>
element, find the<header>
ancestor including itself. -
Code:
WebElement ancestorOrSelfHeader = h1Element.findElement(By.xpath("./ancestor-or-self::header[@id='mainHeader']"));
-
Action: Print the
id
of the header.
-
Objective: From the
-
Descendant-Or-Self Axis (
descendant-or-self
):-
Objective: Find all
<p>
tags within the<section>
withclass='content'
. -
Code:
java.util.List<WebElement> paragraphs = contentSection.findElements(By.xpath(".//descendant-or-self::p"));
- Action: Iterate through the list and print each paragraph’s text.
-
Objective: Find all
-
Following Axis (
following
):-
Objective: From the
<section>
withclass='content'
, find the<footer>
that follows it. -
Code:
WebElement footerAfterContent = driver.findElement(By.xpath("//section[@class='content']/following::footer[@id='mainFooter']"));
- Action: Print the footer text.
-
Objective: From the
-
Preceding Axis (
preceding
):-
Objective: From the
<section>
withclass='content'
, find the<header>
that precedes it. -
Code:
WebElement headerBeforeContent = driver.findElement(By.xpath("//section[@class='content']/preceding::header[@id='mainHeader']"));
- Action: Print the header text.
-
Objective: From the
Summary of XPath Axes
Understanding and effectively utilizing XPath axes can significantly enhance your ability to navigate and interact with web elements in Selenium WebDriver. Here’s a quick recap of the axes covered:
Axis | Description | Use Case Example |
---|---|---|
child |
Selects direct children of the current node. |
Finding an <input> within a specific
<div> .
|
parent |
Selects the immediate parent of the current node. |
Locating the <form> containing a specific
<input> .
|
ancestor |
Selects all ancestors (parents, grandparents, etc.) of the node. |
Traversing from an <input> to its
containing <div> .
|
descendant |
Selects all descendants (children, grandchildren, etc.) of the node. |
Searching for a nested <a> within a
<div> .
|
following-sibling |
Selects all siblings after the current node. |
Finding a <button> that follows a
<label> .
|
preceding-sibling |
Selects all siblings before the current node. |
Locating a <button> that precedes another
<button> .
|
self |
Selects the current node itself. | Confirming the tag and attributes of the current element. |
ancestor-or-self |
Selects all ancestors including the current node. |
From a <span> , finding its
<div> ancestor.
|
descendant-or-self |
Selects all descendants including the current node. |
From a <section> , finding all
<p> tags within it.
|
following |
Selects everything in the document after the closing tag of the node. |
Finding a <footer> that comes after a
<section> .
|
preceding |
Selects everything in the document before the opening tag of the node. |
Locating a <header> that appears before a
<section> .
|
Conclusion
Mastering XPath axes is pivotal for efficient and reliable web automation using Selenium WebDriver. By leveraging the relationships defined by XPath axes, you can navigate complex DOM structures with ease, ensuring your test scripts are both robust and maintainable. Remember to combine XPath axes with other XPath features like functions and logical operators to craft precise and effective element locators.