If I have a set of elements:
Text 1
I think this will work for you.
var deepestLevel = 0;
var deepestLevelText = "";
function findDeepNested(element, currentLevel) {
if ((element.children().length == 0) && (deepestLevel < currentLevel)) {
// No children and current level is deeper than previous most nested level
deepestLevelText="<li>" + element.text() + "</li>";
}
else { // there are children, keep diving
element.children().each( function () {
findDeepNested($(this), currentLevel + 1);
});
}
}
$(".start").each( function () {
deepestLevel = 0;
deepestLevelText = "";
findDeepNested($(this), 0);
$("#results").append(deepestLevelText);
});
Fiddle
Another method(possibly not so fast) with minimal lines of code could be as follows:
var all_matched_elements = $(":contains('" + text_to_search + "')");
var all_parent_elements = $(all_matched_elements).parents();
var all_deepest_matches = $(all_matched_elements).not(all_parent_elements);
This method is especially useful if your text is inside an element that also has other children elements as follows:
<div class="start">
<div>
<div>Text 1<span>Alpha Beta Gamma</span></div>
</div>
</div>
However, the above code would not work for a DOM that looks like following:
<div class="start">
<div>
<div id="zzz">search-text
<div id="aaa">search-text</div>
</div>
</div>
</div>
In above case, it would only select inner-most with id "#aaa". It would drop id "#zzz". If one wants to also choose with id "#zzz" then code at the start has to be modified to drop elements from "all_parent_elements" whose direct text also matches search text.
You need to use the :last filter
$('.start').find(':last');
Working fiddle: http://jsfiddle.net/BmEzd/1/
Here's an implementation that uses a treeWalk function I had written earlier and then wraps it in a jquery method that finds the deepest descendant of each item in the passed in jQuery object and returns a new jQuery object containing those nodes.
A solution with recursion and lots of jQuery can be done with lots less code, but it will likely be slower. This is based on a generic native JS tree walk function that walks a tree.
Working demo with more complicated HTML test case than the OP's HTML: http://jsfiddle.net/jfriend00/8tC3a/
$.fn.findDeepest = function() {
var results = [];
this.each(function() {
var deepLevel = 0;
var deepNode = this;
treeWalkFast(this, function(node, level) {
if (level > deepLevel) {
deepLevel = level;
deepNode = node;
}
});
results.push(deepNode);
});
return this.pushStack(results);
};
var treeWalkFast = (function() {
// create closure for constants
var skipTags = {"SCRIPT": true, "IFRAME": true, "OBJECT": true, "EMBED": true};
return function(parent, fn, allNodes) {
var node = parent.firstChild, nextNode;
var level = 1;
while (node && node != parent) {
if (allNodes || node.nodeType === 1) {
if (fn(node, level) === false) {
return(false);
}
}
// if it's an element &&
// has children &&
// has a tagname && is not in the skipTags list
// then, we can enumerate children
if (node.nodeType === 1 && node.firstChild && !(node.tagName && skipTags[node.tagName])) {
node = node.firstChild;
++level;
} else if (node.nextSibling) {
node = node.nextSibling;
} else {
// no child and no nextsibling
// find parent that has a nextSibling
--level;
while ((node = node.parentNode) != parent) {
if (node.nextSibling) {
node = node.nextSibling;
break;
}
--level;
}
}
}
}
})();
var deeps = $(".start").findDeepest();
deeps.each(function(i,v){
$("#results").append(
$("<li>").html($(v).prop("tagName") + " " + $(v).html())
);
});
<script type="text/javascript">
function findFarthestNode(node)
{
if(node.parent().length && node.parent().prop("tagName") != "BODY")
return findFarthestNode(node.parent());
return node;
}
jQuery(document).ready(function() {
node = jQuery("span");
farthest = findFarthestNode(node);
console.log(farthest);
});
</script>