Reputation: 140
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?
Upvotes: 2
Views: 112
Reputation: 135187
Array.prototype.flatMap is an effective tool for flattening trees (like DOM) to an array of values (like a list of elements) -
function getElementsByClassName (node, query) {
function matchAll (children) {
return Array
.from(children)
.flatMap(c => getElementsByClassName(c, query))
}
if (node.classList && node.classList.contains(query))
return [ node, ...matchAll(node.childNodes) ]
else
return matchAll(node.childNodes)
}
const result =
getElementsByClassName(document, "targetClassName")
console.log(result)
// [ <div class="somediv targetClassName">…</div>
// , <span class="targetClassName">yay1</span>
// , <span class="targetClassName">yay2</span>
// , <span class="targetClassName">yay3</span>
// ]
<div class="somediv targetClassName">
<div class="innerdiv">
<span class="targetClassName">yay1</span>
</div>
</div>
<div class="somediv">
<div class="innerdiv">
<span class="targetClassName">yay2</span>
</div>
</div>
<div class="somediv">
<div class="innerdiv">
<span class="targetClassName">yay3</span>
</div>
</div>
Upvotes: 2
Reputation: 287950
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>
Upvotes: 1