How do I select the innermost element?
Solution 1:
For single path just find the element that doesn't have child nodes:
$('body *:not(:has("*"))');
Or, in your more specific case $('#cell0 *:not(:has("*"))');
For multiple paths - what if there are multiple equally nested nodes? This solution will give you an array of all nodes with highest number of ancestors.
var all = $('body *:not(:has("*"))'), maxDepth=0, deepest = [];
all.each( function(){
var depth = $(this).parents().length||0;
if(depth>maxDepth){
deepest = [this];
maxDepth = depth;
}
else if(depth==maxDepth){
deepest.push(this);
}
});
Again, in your situation you probably want to get to table cells' deepest elements, so you're back to a one-liner:
$('#table0 td *:not(:has("*"))');
- this will return a jQuery object containing all the innermost child nodes of every cell in your table.
Solution 2:
I'd do this through a single recursive function:
// Returns object containing depth and element
// like this: {depth: 2, element: [object]}
function findDeepestChild(parent) {
var result = {depth: 0, element: parent};
parent.children().each(
function(idx) {
var child = $(this);
var childResult = findDeepestChild(child);
if (childResult.depth + 1 > result.depth) {
result = {
depth: 1 + childResult.depth,
element: childResult.element};
}
}
);
return result;
}
Solution 3:
This question has a simple and good answer when for jQuery. However, I was looking for an elegant solution without it. Since :has
is not supported in any browser yet, I came up with this one here.
Array.from( document.querySelectorAll( 'body *' ) )
.filter( e => !e.children.length );
Solution 4:
Well, starting from a basis that you have no idea where this "deepest" node is, you could do something like this:
$.fn.deepest = function() {
var depth = 0;
this.find('*').each(function() {
var d = $(this).parents().length;
depth = Math.max(d, depth);
});
return this.find('*').filter(function() {
return this.parents().length === depth;
});
});
Then
var $deepest = $('body').deepest();
would (excepting the 12 bugs probably in my code) would be a jQuery object for the set of deepest elements.
edit — one of my dozen bugs is that this doesn't take into account the depth of the starting node(s) - that'd be a trick to figure out. Might be better to refactor it so that it finds the deepest of the originally-selected list:
$.fn.betterDeepest = function() {
var depth = 0;
this.each(function() {
depth = Math.max(depth, $(this).parents().length);
});
return this.filter(function() { return $(this).parents().length === depth; });
});
and to get the deepest on the page you'd do:
var deepest = $('*').betterDeepest();
Solution 5:
With a single path:
var elem = $('#table0'),
next;
while ((next = elem.children(':first')).length > 0)
elem = next;
// elem now points to the deepest child of table0
If I get time, I'll update my answer with the code for multiple paths.