DEV Community

Cover image for HTML element to absolute XPath selector - JavaScript
Ab. Karim
Ab. Karim

Posted on • Edited on

5 1

HTML element to absolute XPath selector - JavaScript

XPath selector is convenient in some situations, including a page that randomly generates a new id and class and many more. Let's see how we can get the full XPath position by using an element.

*we will use the child to parent architecture. *

Create a function that receive an element. We will do all work in this function

function getXPath(element) {
   // Selector
    let selector = '';

  // ... Add code here
}
Enter fullscreen mode Exit fullscreen mode

Get tag name of current element

// Get element tag name 
        const tagName = currentElement.tagName.toLowerCase();
Enter fullscreen mode Exit fullscreen mode

Get parent element

  // Get parent element
        const parentElement = currentElement.parentElement;
Enter fullscreen mode Exit fullscreen mode

Is current element is the only child of parentElement if true then add tag to selector;

 // Count children
        if (parentElement.childElementCount > 1) {

        } else {
            //* Current element has no siblings
            // Append tag to selector
            selector = `/${tagName}${selector}`;
        }
Enter fullscreen mode Exit fullscreen mode

If not the only child get other child and filter elements from these elements, that has the same tagname of current tag

 // Count children
        if (parentElement.childElementCount > 1) {


           // Get children of parent element
            const parentsChildren = [...parentElement.children];
            // Count current tag 
            let tag = [];
            parentsChildren.forEach(child => {
                if (child.tagName.toLowerCase() === tagName) tag.push(child) // Append to tag
            })


        } else {
            //* Current element has no siblings
            // Append tag to selector
            selector = `/${tagName}${selector}`;
        }
Enter fullscreen mode Exit fullscreen mode

Now check, is the current element only of type in parent. If so, add to selector /tagName

 // Count children
        if (parentElement.childElementCount > 1) {

           // ...
          // Is only of type
            if (tag.length === 1) {
                // Append tag to selector
                selector = `/${tagName}${selector}`;
            }


        } else {
            //* Current element has no siblings
            // Append tag to selector
            selector = `/${tagName}${selector}`;
        }
Enter fullscreen mode Exit fullscreen mode

If not then add tag position like /tagName[number]

 // Count children
        if (parentElement.childElementCount > 1) {

           // ...
          // Is only of type
            if (tag.length === 1) {
                // Append tag to selector
                selector = `/${tagName}${selector}`;
            } else {
                // Get position of current element in tag
                const position = tag.indexOf(currentElement) + 1;
                // Append tag to selector
                selector = `/${tagName}[${position}]${selector}`;
            }


        } else {
            //* Current element has no siblings
            // Append tag to selector
            selector = `/${tagName}${selector}`;
        }
Enter fullscreen mode Exit fullscreen mode

Now we need to repeate this method until we found html tag. Add a while loop. And some variable to help

 function getXPath(element) {
    //...

    // Loop handler
    let foundRoot;
    // Element handler
    let currentElement = element;

       // Do action until we reach html element
    do {
        // Get element tag name 
        const tagName = currentElement.tagName.toLowerCase();
// ...

        // Set parent element to current element
        currentElement = parentElement;
        // Is root  
        foundRoot = parentElement.tagName.toLowerCase() === 'html';
        // Finish selector if found root element
        if(foundRoot) selector = `/html${selector}`;
    }
    while (foundRoot === false);

Enter fullscreen mode Exit fullscreen mode

And finally return selector

    // Return selector
    return selector;
Enter fullscreen mode Exit fullscreen mode

Here is the full code. element-to-path.js

/**
 * Get absolute xPath position from dom element
 * xPath position will does not contain any id, class or attribute, etc selector
 * Because, Some page use random id and class. This function should ignore that kind problem, so we're not using any selector
 * 
 * @param {Element} element element to get position
 * @returns {String} xPath string
 */
 function getXPath(element) {
    // Selector
    let selector = '';
    // Loop handler
    let foundRoot;
    // Element handler
    let currentElement = element;

    // Do action until we reach html element
    do {
        // Get element tag name 
        const tagName = currentElement.tagName.toLowerCase();
        // Get parent element
        const parentElement = currentElement.parentElement;

        // Count children
        if (parentElement.childElementCount > 1) {
            // Get children of parent element
            const parentsChildren = [...parentElement.children];
            // Count current tag 
            let tag = [];
            parentsChildren.forEach(child => {
                if (child.tagName.toLowerCase() === tagName) tag.push(child) // Append to tag
            })

            // Is only of type
            if (tag.length === 1) {
                // Append tag to selector
                selector = `/${tagName}${selector}`;
            } else {
                // Get position of current element in tag
                const position = tag.indexOf(currentElement) + 1;
                // Append tag to selector
                selector = `/${tagName}[${position}]${selector}`;
            }

        } else {
            //* Current element has no siblings
            // Append tag to selector
            selector = `/${tagName}${selector}`;
        }

        // Set parent element to current element
        currentElement = parentElement;
        // Is root  
        foundRoot = parentElement.tagName.toLowerCase() === 'html';
        // Finish selector if found root element
        if(foundRoot) selector = `/html${selector}`;
    }
    while (foundRoot === false);

    // Return selector
    return selector;
}
Enter fullscreen mode Exit fullscreen mode

Example

const button = document.querySelector("#main > div:nth-child(27) > a.w3-left.w3-btn");

getXPath(button)
Enter fullscreen mode Exit fullscreen mode

returns

'/html/body/div[7]/div[1]/div[1]/div[4]/a[1]'
Enter fullscreen mode Exit fullscreen mode

To learn more about Xpath https://www.w3schools.com/xml/xpath_intro.asp

My github profile

Please give a feedback, what are you thinking about this? and do you had a better solution?

Image of Datadog

Create and maintain end-to-end frontend tests

Learn best practices on creating frontend tests, testing on-premise apps, integrating tests into your CI/CD pipeline, and using Datadog’s testing tunnel.

Download The Guide

Top comments (0)

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more