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.