DEV Community

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

Posted on • Edited on

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?

Top comments (0)