Someone else created a jQuery plugin from the answer as well, but it doesn't have all of the hallmarks of a robust jQuery method; such as returning a collection, filtering by an optional selector, and support for proper chaining.
I needed the practice anyway, so I added these features; but I'll give credit where it is due, since the basic idea was not my own.
Download it: git my gist.
Description: Get the deepest descendants matching an optional selector of each element in the set of matched elements.
A string containing a selector expression to match elements against.
Given a jQuery object that represents a set of DOM elements, the
.deepest() method allows us to search through the children of these elements in the DOM tree and construct a new jQuery object from the matching elements.
.deepest() method is like
.find() because it can traverse down multiple levels to select descendant elements (grandchildren, etc.), but it differs from
.find() in that it only returns the deepest child elements that match an optional selector for each element in the set of matched elements. Note also that like most jQuery methods,
.deepest() does not return text nodes.
.deepest() method optionally accepts a selector expression of the same type that we can pass to the
$() function. If the selector is supplied, the elements will be filtered by testing whether they match it.
Consider a page with several blocks each containing nested elements of various depths:
<div id="nestedExample"> <div class="set"> <div> <div></div> </div> </div> <div class="set empty"></div> <div class="set"> <div> <div> <p class="strong">One</p> </div> </div> </div> <div class="set"> <div> <div> <p class="strong">Two</p> </div> </div> </div> <div class="set"> <div> <p class="strong">Buckle my shoe</p> </div> </div> <div class="set"> <div> <div> <div> <div id="deepest"></div> </div> </div> </div> </div> </div>
If we select the container, we can find its deepest child:
The result of this call is a light gray background behind the most deeply nested child element. Since we do not supply a selector expression, the truly deepest element is part of the returned jQuery object. If there were multiple elements at the same depth, all of them would be included.
If we supply a selector, the deepest elements that match* will be collected:
*These may not be the deepest elements, since this method doesn't find the deepest elements first and then filter by selector. It applies the filter first and finds the deepest instance of that selector.
Elements collected are based on their depth from the selected element in the original set, not simply if they are the deepest elements of their immediate parents in that they have no child elements of their own.
If we pass in the
.strong selector, the returned collection will only include the elements containing text "One" and "Two", while the element containing "Buckle my shoe" is excluded*:
*This is because its depth relative to the selected element in the original set is shallower than the others, even though all of them are the deepest in that they have no child elements.
If our original set of matched elements contains multiple items, the returned collection will include the deepest matches for each of those items:
Because this is considered a filtering method, you may use
.end() to return the original set of matched elements to its previous state for chaining:
$('.set').deepest().css('border-width', '3px').end().css('border-color', 'Red');
Here is a demonstration showing all of the above examples applied at once:
Download it: git my gist.