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

I want to select a common parent from 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 an unknown class in this scenario. I don't know the actual tags or class names. I only know that the nest item contains "Sometext". I know it can be done through a loop using jQuery / Javascript, but is there a CSS selector I can use with jQuery to find this? I have tried using a combination of closest (), parent (), parentUntil (), but I cannot get to this item.

Thank!

+3


source to share


2 answers


First you need to make sure you are only matching leaf nodes (nodes with no child nodes), so use:

:not(:has(*))

      

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

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

      

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

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

      

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

Then you need to find the first ancestor (first match) containing all matches:

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

      

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



Based on a suggestion from David Thomas, here it presents a couple of jQuery extensions ( commonParents()

and commonParent()

) that could be used in the future for people:

To find all common parents in 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 optimizes sharing first()

    in commonParent

    with commonParents

    filter()

    , and it only calls code in commonParents

    before the first match, so it commonParent

    doesn't need to be made more efficient.
+3


source


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

You can even wrap it as a 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 e.g. '.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>
      

Run code


0


source







All Articles