使用 jQuery 和 CSS 选择器选择嵌套元素的共同父/祖先

Posted

技术标签:

【中文标题】使用 jQuery 和 CSS 选择器选择嵌套元素的共同父/祖先【英文标题】:Select common parent/ancestor of nested elements with jQuery and CSS Selector 【发布时间】:2015-02-27 21:10:40 【问题描述】:

我希望选择多个嵌套元素的公共父级,我只知道其内部文本。

例如在下面的代码中:

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

我想获取在这种情况下类未知的最近元素(公共父级)。我不知道实际的标签或类名。我只知道嵌套元素包含“Sometext”。我知道这可以通过使用 jQuery/javascript 的循环来完成,但是是否有一个 CSS 选择器可以与 jQuery 一起使用来找到它?我尝试使用最接近(),父母(),父母Until()的组合,但我似乎无法到达这个元素。

谢谢!

【问题讨论】:

所以你的意思是,如果第一个“Sometext”的直接父级是class="unknown",它会忽略这一点并仍然选择***的,因为这对所有“Sometext”都是通用的? 是的,没错。 【参考方案1】:

首先,你需要确保你只匹配叶子节点(没有子节点的节点),所以使用:

:not(:has(*))

所以要找到所有的完全匹配(只是叶节点),使用:

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

或仅对所有元素使用组合过滤器(添加对 0 个子元素的检查):

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

注意: 我还没有测试过这两个选项中哪个最快。

然后你需要找到(第一个匹配的)第一个祖先,它包含所有匹配:

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

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

根据 David Thomas 的建议,这里是一对 jQuery 扩展(commonParents()commonParent()),将来可能会被人们使用:

要查找 jQuery 集合的所有共同父项,请使用 `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/

要查找 jQuery 集合的最近公共父级,请使用 commonParent()

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

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

注意事项:

jQuery 优化了first()commonParent 中与commonParents filter() 的组合使用,它只调用commonParents 中的代码直到第一次匹配,所以@ 987654339@ 不需要提高效率。

【讨论】:

干得好,这是用插件形式重写的方法的一个版本:JS Fiddle demo。 @David Thomas:很好地包装为扩展。我不认为这是经常需要的,以使其成为扩展,但我会在答案中添加我自己的扩展。 是的,我想它可能是相对不经常出现的问题之一,但我看到了你的答案,并有一些时间......现在,如果你可以工作,可能会搜索-在您的答案中加上长尾应该令人印象深刻。 :) 真棒回答 TrueBlueAusie。感谢您详细解释该过程。【参考方案2】:

这应该可以完成工作。您基本上找到所有匹配元素的所有相关父级,获取每个集合的交集,然后抓取第一个以获取嵌套最多的公共父级。

您甚至可以将其封装为 jquery 插件。

if(console && console.clear) console.clear();

// create a handy intersection method for Arrays
// see http://***.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>

【讨论】:

感谢 Rhumborl。如果我真的知道父母的班级,这将起作用。但是,我命名为未知,因为它会有所不同。这是我想出的以获得我需要的东西,但我认为它可以改进/重构:var jq = $('*').find(':last:contains("Sometext")'); var prnt = $(jq[0]); jq.each(function () prnt = prnt.parents().add(prnt).has(this).last(); ); return prnt; 哦,对了,所以类位实际上是无关紧要的?如果你只想要 共同的父级,只需将 parentClass 设置为 *

以上是关于使用 jQuery 和 CSS 选择器选择嵌套元素的共同父/祖先的主要内容,如果未能解决你的问题,请参考以下文章

用sass选择器嵌套的时候,如何表示直接子元素

JQuery 在嵌套元素上查找没有选择器的文本节点仅适用于 .contents() 和 filter()

jquery使用css选择器来选取元素吗

Jquery为嵌套表选择td

使用css选择器从元素中获取文本,不包括嵌套元素内的文本

如何防止 jQuery 选择器包含嵌套元素?