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
}
Get tag name of current element
// Get element tag name
const tagName = currentElement.tagName.toLowerCase();
Get parent element
// Get parent element
const parentElement = currentElement.parentElement;
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}`;
}
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}`;
}
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}`;
}
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}`;
}
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);
And finally return selector
// Return selector
return selector;
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;
}
Example
const button = document.querySelector("#main > div:nth-child(27) > a.w3-left.w3-btn");
getXPath(button)
returns
'/html/body/div[7]/div[1]/div[1]/div[4]/a[1]'
To learn more about Xpath https://www.w3schools.com/xml/xpath_intro.asp
Please give a feedback, what are you thinking about this? and do you had a better solution?
Top comments (0)