CSS selector generator

Problem Statement -

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>
        javascriptbucket
        <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)"

To generate the CSS selector, all we have to do is start from the target and trace it 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 1 to it as CSS nth-child is not like an 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(' > ');
}

Test Case

Input:
<div id="root">
  <article>Prepare for interview</article>
  <section>
on
<p> <span>
        javascriptsbucket
        <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)"