Ervin
Ervin

Reputation: 27

JavaScript: recursive function does not work like it should

I'm trying to do a recursive function to find HTMLelement by class, but for some reason it stops after it reaches a child element without children. Here is the code:


var alpha = Obj.elementByClass(document.body, "message");

Obj.elementByClass = function(element, cl) {
   var elementChildren = element.children,
       elementChildrenLength = elementChildren.length;

   for(var num=0; num<elementChildrenLength; num++) {
      if(elementChildren[num].className && elementChildren[num].className.indexOf("cl") > -1) {
         return elementChildren[num];
      }
      else if(elementChildren[num].children.length !=0) {
         return Obj.elementByClass(elementChildren[num], cl);
      }
   }
};

Please don't advice jquery or other libraries, I would like to understand why it stops when it reaches the element without children.

Thanks!

Upvotes: 2

Views: 183

Answers (5)

Arnaud Le Blanc
Arnaud Le Blanc

Reputation: 99921

Non Element nodes (e.g. text nodes) do not have a childNodes property, so trying to read .children.length on them throws an error, which aborts the execution.

You have to verify that the node is an Element:

for(var num=0; num<elementChildrenLength; num++) {
    if (elementChildren[num].nodeType !== 1) {
        continue;
    }
    ...

Or just check that the node has a childNodes property before accessing it:

else if(elementChildren[num].children && elementChildren[num].children.length !=0) {

Upvotes: 1

KooiInc
KooiInc

Reputation: 122906

As far as I can see, you are searching for the first child of the body element with className 'message'. May I suggest this alternative?

document.querySelector('body .message')

The function returns undefined because the first condition AND else if(elementChildren[num].children.length !=0) both are not true. So, nothing to do, exit, nothing returned.

Upvotes: 1

Mitya
Mitya

Reputation: 34556

Obj.elementByClass = function(element, cl) {
   for(var i=0, len = element.children.length; i<len; i++) {
    if (element.children[i].className && new RegExp('\\b'+cl+'\\b').test(element.children[i].className))
       return element.children[i];
    else if(element.children[i].children.length)
       return Obj.elementByClass(element.children[i], cl);
   }
};

A couple of points:

  • you were calling the function before it was defined (and, since this is not a hoisted function, that would error)
  • the test for the class is now tighter, because it now disallows the class name found in other classnames that share the same word (the check is now done with REGEXP, using word bounaries)
  • your indexOf() was looking explicitly for the string "cl", where I think you meant the argument cl
  • I cut it down a bit for readability
  • as the code is currently, you return only the first matching node, not all of them

Upvotes: 1

Jimmery
Jimmery

Reputation: 10139

if elementChildrenLength is equal to zero then the entire for loop block will not execute.

best way to avoid this is to wrap the for loop block with something along the lines of:

if(elementChildrenLength==0){
    return 0;
}else{
   for(var num=0; num<elementChildrenLength; num++) {
       //for loop block here
   }
}

Upvotes: 1

Anthony Grist
Anthony Grist

Reputation: 38345

Because the for loop never executes when the element doesn't have children, due to both num and elementChildrenLength being 0 and the condition num<elementChildrenLength never being true.

Upvotes: 1

Related Questions