简体   繁体   English

找到最近的具有特定类的祖先元素

[英]Find the closest ancestor element that has a specific class

How can I find an element's ancestor that is closest up the tree that has a particular class, in pure JavaScript ?如何在纯 JavaScript 中找到最靠近具有特定类的树的元素的祖先? For example, in a tree like so:例如,在这样的树中:

<div class="far ancestor">
    <div class="near ancestor">
        <p>Where am I?</p>
    </div>
</div>

Then I want div.near.ancestor if I try this on the p and search for ancestor .然后我想要div.near.ancestor如果我在p上尝试这个并搜索ancestor

Update: Now supported in most major browsers更新:现在大多数主流浏览器都支持

document.querySelector("p").closest(".near.ancestor")

Note that this can match selectors, not just classes请注意,这可以匹配选择器,而不仅仅是类

https://developer.mozilla.org/en-US/docs/Web/API/Element.closest https://developer.mozilla.org/en-US/docs/Web/API/Element.closest


For legacy browsers that do not support closest() but have matches() one can build selector-matching similar to @rvighne's class matching:对于不支持closest()但有matches()旧浏览器,可以构建类似于@rvighne 的类匹配的选择器匹配:

function findAncestor (el, sel) {
    while ((el = el.parentElement) && !((el.matches || el.matchesSelector).call(el,sel)));
    return el;
}

This does the trick:这是诀窍:

function findAncestor (el, cls) {
    while ((el = el.parentElement) && !el.classList.contains(cls));
    return el;
}

The while loop waits until el has the desired class, and it sets el to el 's parent every iteration so in the end, you have the ancestor with that class or null . while 循环一直等到el具有所需的类,并且每次迭代都会将el设置为el的父级,因此最后,您将拥有该类或null的祖先。

Here's a fiddle , if anyone wants to improve it.这是一个小提琴,如果有人想改进它。 It won't work on old browsers (ie IE);它不适用于旧浏览器(即 IE); see this compatibility table for classList .请参阅classList 的兼容性表 parentElement is used here because parentNode would involve more work to make sure that the node is an element.此处使用parentElement是因为parentNode将涉及更多工作以确保该节点是一个元素。

Use element.closest()使用element.closest()

https://developer.mozilla.org/en-US/docs/Web/API/Element/closest https://developer.mozilla.org/en-US/docs/Web/API/Element/closest

See this example DOM:请参阅此示例 DOM:

<article>
  <div id="div-01">Here is div-01
    <div id="div-02">Here is div-02
      <div id="div-03">Here is div-03</div>
    </div>
  </div>
</article>

This is how you would use element.closest:这是您使用 element.closest 的方式:

var el = document.getElementById('div-03');

var r1 = el.closest("#div-02");  
// returns the element with the id=div-02

var r2 = el.closest("div div");  
// returns the closest ancestor which is a div in div, here is div-03 itself

var r3 = el.closest("article > div");  
// returns the closest ancestor which is a div and has a parent article, here is div-01

var r4 = el.closest(":not(div)");
// returns the closest ancestor which is not a div, here is the outmost article

Based on the the8472 answer and https://developer.mozilla.org/en-US/docs/Web/API/Element/matches here is cross-platform 2017 solution:基于the8472 答案https://developer.mozilla.org/en-US/docs/Web/API/Element/matches这里是跨平台 2017 解决方案:

if (!Element.prototype.matches) {
    Element.prototype.matches =
        Element.prototype.matchesSelector ||
        Element.prototype.mozMatchesSelector ||
        Element.prototype.msMatchesSelector ||
        Element.prototype.oMatchesSelector ||
        Element.prototype.webkitMatchesSelector ||
        function(s) {
            var matches = (this.document || this.ownerDocument).querySelectorAll(s),
                i = matches.length;
            while (--i >= 0 && matches.item(i) !== this) {}
            return i > -1;
        };
}

function findAncestor(el, sel) {
    if (typeof el.closest === 'function') {
        return el.closest(sel) || null;
    }
    while (el) {
        if (el.matches(sel)) {
            return el;
        }
        el = el.parentElement;
    }
    return null;
}

@rvighne solution works well, but as identified in the comments ParentElement and ClassList both have compatibility issues. @rvighne 解决方案运行良好,但正如评论中ParentElementClassList都存在兼容性问题。 To make it more compatible, I have used:为了使其更兼容,我使用了:

function findAncestor (el, cls) {
    while ((el = el.parentNode) && el.className.indexOf(cls) < 0);
    return el;
}
  • parentNode property instead of the parentElement property parentNode属性而不是parentElement属性
  • indexOf method on the className property instead of the contains method on the classList property. className属性上的indexOf方法,而不是classList属性上的contains方法。

Of course, indexOf is simply looking for the presence of that string, it does not care if it is the whole string or not.当然, indexOf 只是寻找该字符串的存在,它并不关心它是否是整个字符串。 So if you had another element with class 'ancestor-type' it would still return as having found 'ancestor', if this is a problem for you, perhaps you can use regexp to find an exact match.因此,如果您有另一个具有“祖先类型”类的元素,它仍然会返回找到“祖先”,如果这对您来说是个问题,也许您可​​以使用正则表达式来查找精确匹配项。

This solution should work for IE9 and up.此解决方案应该适用于 IE9 及更高版本。

It's like jQuery's parents() method when you need to get a parent container which might be up a few levels from the given element, like finding the containing <form> of a clicked <button> .当您需要获取一个父容器时,它就像 jQuery 的 parents() 方法,该父容器可能比给定元素高几个级别,例如找到单击的<button>的包含<form> <button> Looks through the parents until the matching selector is found, or until it reaches the <body> .查看父级,直到找到匹配的选择器,或者直到它到达<body> Returns either the matching element or the <body> .返回匹配元素或<body>

function parents(el, selector){
    var parent_container = el;
    do {
        parent_container = parent_container.parentNode;
    }
    while( !parent_container.matches(selector) && parent_container !== document.body );

    return parent_container;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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