简体   繁体   中英

Select common parent/ancestor of nested elements with jQuery and CSS Selector

I am looking to select the common parent of multiple nested elements for which I only know the inner text.

For example, in the following code:

<unknown>       
    <unknown class="unknown">
        ....
        <unknown>
            <unknown>Sometext</unknown>
        </unknown>
        <unknown>
            <unknown>Sometext</unknown>
        </unknown>
        <unknown>
            <unknown>Sometext</unknown>
        </unknown>
        ....
    </unknown>
</unknown>

I would like to get the closest element (common parent) that has class unknown in this scenario. I do not know the actual tags or class names. I only know that the nest element contains the "Sometext". I know this can be done through a loop using jQuery/Javascript, but is there a CSS selector that I can use with jQuery to find this? I tried using a combination of closest(), parents(), parentsUntil() but I can't seem to get to this element.

Thank you!

First, you need to ensure you only match the leaf nodes (nodes with no child nodes), so use:

:not(:has(*))

So to find all the exact matches (just the leaf nodes), use:

var matches = $(':not(:has(*))').filter(function () {
    return $(this).text() == "Sometext";
});

or just using a combined filter on all elements (with an added check for 0 children):

var matches = $('*').filter(function () {
     return !$(this).children().length && $(this).text() == "Sometext";
});

Note: I have not yet tested which of these two options is fastest.

Then you need to find the first ancestor (of the first match), that contains all the matches:

var commonparent = matches.first().parents().filter(function () {
    return $(this).find(matches).length == matches.length;
}).first();

JSFiddle: http://jsfiddle.net/TrueBlueAussie/v4gr1ykg/

Based on David Thomas' suggestion, here it is as a pair of jQuery extensions ( commonParents() and commonParent() ) that may be of future use for people:

To find all common parents of a jQuery collection use `commonParents()':

$.fn.commonParents = function (){
    var cachedThis = this;
    return cachedThis.first().parents().filter(function () {
        return $(this).find(cachedThis).length === cachedThis.length;
    });
};

JSFiddle: (commonParents): http://jsfiddle.net/TrueBlueAussie/v4gr1ykg/3/

To find the closest common parent of a jQuery collection use commonParent() :

$.fn.commonParent = function (){
    return $(this).commonParents().first();
};

JSFiddle: (commonParent): http://jsfiddle.net/TrueBlueAussie/v4gr1ykg/2/

Notes:

  • jQuery optimises the combined use of first() in commonParent with the commonParents filter() and it only calls the code in commonParents until a first match is made, so commonParent does not need to be made more efficient.

This should do the job. You basically find all the relevant parents of all the matching elements, get the intersection of each of those sets, then grab the first one to get the most nested common parent.

You can even wrap it up as as jquery plugin.

 if(console && console.clear) console.clear(); // create a handy intersection method for Arrays // see http://stackoverflow.com/a/16227294/1901857 Array.prototype.intersect = function(arr) { var a = this, b = arr; var t; if (b.length > a.length) t = b, b = a, a = t; // indexOf to loop over shorter return a.filter(function (e) { return b.indexOf(e) > -1; }); }; ;(function($) { $.fn.commonParents = function(selector) { // find all relevant parents for each element and get set intersection // pushStack means you can use end() etc in chaining correctly return this.pushStack(sometexts.get().reduce(function(prevParents, el) { // common parents for this element - note the lowest level parent is first var parents = $(el).parents(selector || '*').get(); // intersect with the previous value (or itself if first) return (prevParents || parents).intersect(parents); }, null), "commonParents", arguments); }; })(jQuery); // text to search for var search = "Sometext"; // parent selector to filter parents by eg '.unknown' - use null for all parents var parentSelector = null; // find everything containing search var sometexts = $(":contains('" + search + "')").filter(function() { return $(this).text() == search; }); // grab the first common parent - the lowest level one - or null if there isn't one var commonParent = sometexts.commonParents(parentSelector).get(0); console.log(commonParent); 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> <div> <div class="unknown test"> <div class="unknown test2"> <div class="unknown"> <div>Sometext</div> </div> <div> <div>Sometext</div> </div> <div> <div>Sometext</div> </div> </div> </div> </div> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM