Implement getByClassNameHierarchy() function in JavaScript

Write a function getByClassNameHierarchy() in javaScript that takes a path of class names and returns the last element of that path.

Example

Input:
<div class="a" id="a-1">
  <div class="b" id="b-1">
    <div class="c" id="c-1"/>
    <div class="c" id="c-2"/>
  </div>
  <div class="c" id="c-3"/>
</div>

getByClassNameHierarchy("a>b>c");

Output:
[<div class="c" id="c-1"></div>, <div class="c" id="c-2"></div>]

This function is the extended version of getElementByClassName().

Using the following approach we can solve this.

  • Split the input path in an array of classes.
  • Use an index tracker to track the current class at different levels.
  • Recursively traverse the DOM from the root and in each functional call add the following checks,
  • If the element is null then return (terminate further calls).
  • If the current class is the last of the path and it is present in the element, add it to the result as we have reached to required path and return (terminate further calls).
  • In the end, traverse all the children of the current element and if the element has the current class, recursively traverse its child with the next class (increase the index tracker by one), else traverse the child with the first class (index tracker as zero) and keep on finding the path.

For the DOM traversal, we will be using a helper function.

function getByClassNameHierarchy(element, classNames) {
  // get all the classnames
  const classList = classNames.split('>');
  
  // pass the array as a reference
  const result = [];
  
  // traverse the dom from the root
  traverseDOM(element, classList, 0, result);
  
  // return the result
  return result;
}

// helper function
function traverseDOM(element, classNames, index, result){
  // if the element is not present
  if(!element) {
    return;
  }
  
  // get the current class name
  const targetClass = classNames[index];
   
  // if the last class of the classNames
  // and the element contains the class.
  // add the element to the result
  if(index === classNames.length - 1 && element.classList.contains(targetClass)) {
    result.push(element);
    return;
  }
  
  // iterate each children of the element
  for(const child of element.children) {
    // if the child has the class
    // recursively traverse and search for the next class in the child
    // thus increase the index by one
    if(element.classList.contains(targetClass)) {
        traverseDOM(child, classNames, index + 1, result);
    } 
    // else start the search from the scratch in the child
    else {
        traverseDOM(child, classNames, 0, result);
    }
  }
}
Input:
<div class="a" id="root">
  <div class="b" id="b-1">
    <div class="c" id="c-1">
    </div>
    <div class="c" id="c-2">
    </div>
  </div>
  <div class="b" id="b-2">
    <div class="c" id="c-3">
    </div>
  </div>
</div>

console.log(getByClassNameHierarchy(document.getElementById('root'), 'a>b>c'));

Output:
[
  <div class="c" id="c-1"></div>,
  <div class="c" id="c-2"></div>,
  <div class="c" id="c-3"></div>
]