【问题标题】:Select common parent/ancestor of nested elements with jQuery and CSS Selector使用 jQuery 和 CSS 选择器选择嵌套元素的共同父/祖先
【发布时间】: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”都是通用的?
  • 是的,没错。

标签: javascript jquery html jquery-selectors dom-traversal


【解决方案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://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>

【讨论】:

  • 感谢 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 设置为 *
猜你喜欢
  • 2010-11-18
  • 2012-09-22
  • 2019-07-27
  • 2011-01-13
  • 2023-03-26
  • 2013-09-15
  • 2013-04-12
  • 1970-01-01
相关资源
最近更新 更多