Search code examples
javascripthtmldomtraversal

Javascript HTML .children traversal


I'm trying to go through all of the elements in the document and pull the ones with a target class name. Importantly, I'm needing to do it without the use of document.getElementsByClassName(className) / document.querySelectorAll, etc. — that's the point of this learning exercise.

Here's the javascript:

var getElementsByClassName = function(className){
  var rootElem = document.body;
  var collectionResult = [];
  if (rootElem.getAttribute("class") == className) {
    collectionResult.push(rootElem);
  };
  var nextTier = function(collectionResult, rootElem) {
    var thisTier = rootElem.children;
    for (i=0; i<thisTier.length; i++) {
      var classes = thisTier[i].getAttribute("class");
      if (classes != undefined && classes.includes(className)) {
        collectionResult.push(thisTier[i]);
      };
      var childrenArray = thisTier[i].children; 
      if (childrenArray.length > 0) {
        nextTier(collectionresult, childrenArray)
      };
    };
  };
  nextTier(collectionResult, rootElem);
  return collectionResult;
};

Here's the section of the HTML structure I'm having trouble with:

<p>
  <div class="somediv">
    <div class="innerdiv">
      <span class="targetClassName">yay</span>
    </div>
   </div>
 </p>

The code works for the rest of the page with any number of non-nested elements. But as soon as var childrenArray = thisTier[i].children get to the div.somediv element, it has childrenArray == undefined rather than pulling the div.innerdiv element.

Am I misunderstanding how element.children works?


Solution

  • You seem to be overcomplicating things.

    function getElementsByClassName(className, root) {
      if(!root) root = document.documentElement;
      return [].reduce.call(root.children, function(arr, child) {
        if(child.classList.contains(className)) arr.push(child);
        return arr.concat(getElementsByClassName(className, child))
      }, []);
    }
    

    function getElementsByClassName(className, root) {
      if(!root) root = document.documentElement;
      return [].reduce.call(root.children, function(arr, child) {
        if(child.classList.contains(className)) arr.push(child);
        return arr.concat(getElementsByClassName(className, child))
      }, []);
    }
    console.log(getElementsByClassName("targetClassName"));
    <div class="somediv targetClassName">
      <div class="innerdiv">
        <span class="targetClassName">yay1</span>
      </div>
    </div>
    <div class="somediv targetClassName">
      <div class="innerdiv targetClassName">
        <span class="targetClassName">yay2</span>
      </div>
    </div>
    <div class="somediv">
      <div class="innerdiv">
        <span class="targetClassName">yay3</span>
      </div>
    </div>