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:

  1. child: Selects all child elements of the current node.
  2. parent: Selects the parent of the current node.
  3. ancestor: Selects all ancestors (parents, grandparents, etc.) of the current node.
  4. descendant: Selects all descendants (children, grandchildren, etc.) of the current node.
  5. following-sibling: Selects all siblings that appear after the current node.
  6. preceding-sibling: Selects all siblings that appear before the current node.
  7. self: Selects the current node itself.
  8. ancestor-or-self: Selects all ancestors and the current node.
  9. descendant-or-self: Selects all descendants and the current node.
  10. following: Selects everything in the document after the closing tag of the current node.
  11. 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:

  1. Child Axis (child):

    • Objective: Locate the <header> element that is a direct child of the <div> with class='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.
  2. Descendant Axis (descendant):

    • Objective: Find the <p> tag within the <article> with id='article2'.
    • Code:
      WebElement article2Paragraph = driver.findElement(By.xpath("//article[@id='article2']/descendant::p"));
      
    • Action: Print the paragraph text.
  3. Ancestor Axis (ancestor):

    • Objective: From the <footer> element, find its ancestor <div> with class='container'.
    • Code:
      WebElement containerDiv = footer.findElement(By.xpath("./ancestor::div[@class='container']"));
      
    • Action: Print the class attribute of the container div.
  4. 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.
  5. 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.
  6. 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.
  7. 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.
  8. Descendant-Or-Self Axis (descendant-or-self):

    • Objective: Find all <p> tags within the <section> with class='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.
  9. Following Axis (following):

    • Objective: From the <section> with class='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.
  10. Preceding Axis (preceding):

    • Objective: From the <section> with class='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.

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.