nick stobie
nick stobie

Reputation: 31

Reimplementing getElementsByClassName using Recursion - Javascript

I'm currently working on learning recursion and am trying to reimplement the getElementsByClassName function by walking the DOM using recursion. I finally feel like I've grasped the concepts but I'm having issues when I push the matching elements into a results array. Heres my code:

var getElementsByClassName = function(className){
  var results = [];
  var domWalker = function(node) {
    for (var i=0; i<node.children.length; i++) {
        if (node.children[i].classList.contains(className)) {
            console.log(node.children[i])
            results.push(node.children[i])
        }

        if (node.children[i].children.length > 0) {
            domWalker(node.children[i])
        }

    }
    return results;
  };
  domWalker(document.body);
  console.log(results)
};

Basically, I need the results array to hold the matching elements it finds in HTML format like so:

[<div class="title"></div>, <button class="click"></button>]

... yet when I push these elements into my results array they change to the: [div.title, button.click] format.

I added the console.log statement above the call to results.push to see if the results appear in the proper format before they are pushed to the array which they do. The results being pushed to the array are the results I'm looking for, they just appear in the wrong format.

Why is push causing the format of my results to change and how can I get around this issue?

Upvotes: 3

Views: 2010

Answers (2)

Nick Salloum
Nick Salloum

Reputation: 2278

I solved this problem once upon a time. Although I haven't read through your solution, here is mine, heavily commented. I hope it helps:

var getElementsByClassName = function(className, node){
  // The empty results array, which gets created whenever the function is
  // called. 
  var results = [];

  // Default the node to the document's body if it isn't set. This way, we can
  // call the function recursively with child elements, but never have to
  // worry about it the first time around.
  node = node || document.body;

  // If the node contains the class list in question, let's push it into the
  // results array.
  if (node.classList && node.classList.contains(className)) {
    results.push(node);
  }

  // Now, let's fetch the child nodes of the current node.
  var children = node.childNodes;

  // If child nodes exist, then we proceed here.
  if (children) {
    // Let's now loop over all child nodes of the current node in question. This
    // way, we'll be able to perform checks on each child node.
    for (var i = 0; i < children.length; i++) {
      // Fetch the i child node.
      var child = children[i];

      // At this point, we want to pass the child node back into the function,
      // implementing recursion. The same checks above will occur, and if the
      // node has the class name, it will be added to the results array from
      // that function call.
      //
      // This returns an array, and we need to merge it with the current set
      // of results, so we concat it.
      //
      // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat
      results = results.concat(getElementsByClassName(className, child));
    }
  }

  // We then return the combined results array from all the recursive function
  // calls!
  return results;
};

Upvotes: 1

FoxInCloud
FoxInCloud

Reputation: 101

node.children[i] holds a reference to the HTML element

console.log() applies an implicit .toString() method giving what you see.

you need this additional code (to be extended to all possible tagNames you find):

var el = node.children[i];
el = '<' + el.tagName + (el.className ? ' class="' + el.className + '"': '') + '/>';
console.log(el);
results.push(el);

Upvotes: 0

Related Questions