Below is a straightforward function that retrieves leaf nodes by considering all nodes, including text nodes (ensuring elements containing text nodes are not returned):
function getLeafNodes(master) {
var nodes = Array.prototype.slice.call(master.getElementsByTagName("*"), 0);
var leafNodes = nodes.filter(function(elem) {
return !elem.hasChildNodes();
});
return leafNodes;
}
View the working demo here.
Note: The use of the .filter()
method requires IE9 compatibility. To support earlier versions of IE, consider implementing a polyfill for .filter()
or opt for manual array iteration.
If excluding text nodes and focusing solely on leaf elements, utilize the following version:
function getLeafNodes(master) {
var nodes = Array.prototype.slice.call(master.getElementsByTagName("*"), 0);
var leafNodes = nodes.filter(function(elem) {
if (elem.hasChildNodes()) {
// Check for child elements
for (var i = 0; i < elem.childNodes.length; i++) {
if (elem.childNodes[i].nodeType == 1) {
// Child element exists - exclude parent element
return false;
}
}
}
return true;
});
return leafNodes;
}
Working demo available here.
For a recursive solution disregarding text nodes, refer to the snippet below:
function getLeafNodes(master) {
var results = [];
var children = master.childNodes;
for (var i = 0; i < children.length; i++) {
if (children[i].nodeType == 1) {
var childLeafs = getLeafNodes(children[i]);
if (childLeafs.length) {
// Concatenate child leaf nodes with current results
results = results.concat(childLeafs);
} else {
// Current node is a leaf
results.push(children[i]);
}
}
}
// If no leaves found at this level, treat current node as leaf
if (!results.length) {
results.push(master);
}
return results;
}
See it in action here.