[jquery] Find closest previous element jQuery

I am wanting something similar to this person, except the element I want to match might not be a direct sibling.

If I had this HTML, for example,

<h3>
    <span>
        <b>Whaddup?</b>
    </span>
</h3>
<h3>
    <span>
        <b>Hello</b>
    </span>
</h3>
<div>
    <div>
        <img />
    </div>
     <span id="me"></span>
</div>
<h3>
    <span>
        <b>Goodbye</b>
    </span>
</h3>

I would want to be able to do something like this:

var link = $("#me").closestPreviousElement("h3 span b");
console.log(link.text()); //"Hello"

Is there an easy way to do this in jQuery?

EDIT: I should have made my specification a little bit clearer. $("#me") may or may not have a parent div. The code should not assume that it does. I don't necessarily know anything about the surrounding elements.

This question is related to jquery

The answer is


No, there is no "easy" way. Your best bet would be to do a loop where you first check each previous sibling, then move to the parent node and all of its previous siblings.

You'll need to break the selector into two, 1 to check if the current node could be the top level node in your selector, and 1 to check if it's descendants match.

Edit: This might as well be a plugin. You can use this with any selector in any HTML:

(function($) {
    $.fn.closestPrior = function(selector) {
        selector = selector.replace(/^\s+|\s+$/g, "");
        var combinator = selector.search(/[ +~>]|$/);
        var parent = selector.substr(0, combinator);
        var children = selector.substr(combinator);
        var el = this;
        var match = $();
        while (el.length && !match.length) {
            el = el.prev();
            if (!el.length) {
                var par = el.parent();
                // Don't use the parent - you've already checked all of the previous 
                // elements in this parent, move to its previous sibling, if any.
                while (par.length && !par.prev().length) {
                    par = par.parent();
                }
                el = par.prev();
                if (!el.length) {
                    break;
                }
            }
            if (el.is(parent) && el.find(children).length) {
                match = el.find(children).last();
            }
            else if (el.find(selector).length) {
                match = el.find(selector).last();
            }
        }
        return match;
    }
})(jQuery);

I know this is old, but was hunting for the same thing and ended up coming up with another solution which is fairly concise andsimple. Here's my way of finding the next or previous element, taking into account traversal over elements that aren't of the type we're looking for:

var ClosestPrev = $( StartObject ).prevAll( '.selectorClass' ).first();
var ClosestNext = $( StartObject ).nextAll( '.selectorClass' ).first();

I'm not 100% sure of the order that the collection from the nextAll/prevAll functions return, but in my test case, it appears that the array is in the direction expected. Might be helpful if someone could clarify the internals of jquery for that for a strong guarantee of reliability.


see http://api.jquery.com/prev/

var link = $("#me").parent("div").prev("h3").find("b");
alert(link.text());

see http://jsfiddle.net/gBwLq/


var link = $("#me").closest(":has(h3 span b)").find('span b').text();