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!
5 Answers 5
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.
Comments
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
}
}
Comments
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 argumentcl - I cut it down a bit for readability
- as the code is currently, you return only the first matching node, not all of them
Comments
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.
Comments
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) {
else ifblock, youreturnsomething as soon as there is a child - but it may be possible that a child doesn't have any descendant with that specific class name. I recommend chceking whether the recursive call in theelse ifblock actually returns an element. As it is now, thereturnbreaks the wholeforloop.