Given a root node and target node, generate a CSS selector from the root to the target. Provide an exact selection.
Example
Input:
<div id="root">
<article>Prepare for interview</article>
<section>
on
<p>
<span>
Learnersbucket
<button>click me!</button>
<button id="target">click me!</button>
</span>
</p>
</section>
</div>
Output:
"div[id='root'] > section:nth-child(2) > p:nth-child(1) > span:nth-child(1) > button:nth-child(2)"
It is quite straightforward to generate the CSS selector, all we have to do is start from the target and trace back to the root (parent).
- Use a while loop and keep iterating till we have found the root of the target.
- In each iteration get the index of the current child in its immediate parent to decide the nth-child position.
- At the end, add the roots tag name. The selector will begin from this.
const generateSelector = (root, target) => {
// trace the selector from target to root
const selectors = [];
// iterate till root parent is found
while (target !== root) {
// get the position of the current element as its parent child
// add one to it ass CSS nth-child is not like array, it starts from 1.
const nthChild = Array.from(target.parentNode.children).indexOf(target) + 1;
const selector = `${target.tagName.toLowerCase()}:nth-child(${nthChild})`;
// add the selector at the front
selectors.unshift(selector);
// move to the parent
target = target.parentNode;
}
// add the root's tag name at the beginning
// with your preferred selector
// id is used here
selectors.unshift(`${target.tagName.toLowerCase()}[id="${target.id}"]`);
// join the path of the selector and return them
return selectors.join(' > ');
}
Input:
<div id="root">
<article>Prepare for interview</article>
<section>
on
<p>
<span>
Learnersbucket
<button>click me!</button>
<button id="target">click me!</button>
</span>
</p>
</section>
</div>
const root = document.getElementById("root");
const target = document.getElementById("target");
console.log(generateSelector(root, target));
Output:
"div[id='root'] > section:nth-child(2) > p:nth-child(1) > span:nth-child(1) > button:nth-child(2)"