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

IT技术 javascript html dom
2021-01-27 16:15:02

如何在纯 JavaScript 中找到最接近具有特定类的树的元素的祖先例如,在这样的树中:

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

然后我想div.near.ancestor如果我尝试这个p并搜索ancestor.

6个回答

更新:现在大多数主流浏览器都支持

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

请注意,这可以匹配选择器,而不仅仅是类

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


对于不支持closest()但有matches()一个的旧浏览器,可以构建类似于@rvighne 的类匹配的选择器匹配:

function findAncestor (el, sel) {
    while ((el = el.parentElement) && !((el.matches || el.matchesSelector).call(el,sel)));
    return el;
}
有一个DOM Level 4 polyfill,closest以及许多更高级的 DOM 遍历功能。您可以单独提取该实现或包含整个包以在您的代码中获得许多新功能。
2021-03-14 16:15:02
2016 年 6 月:看起来所有浏览器都没有赶上
2021-03-16 16:15:02
Edge 15 有支持,应该涵盖所有主要浏览器。除非你算上 IE11,但那不会收到任何更新
2021-03-16 16:15:02
@kleinfreund - IE、Edge 或 Opera mini 仍不受支持。caniuse.com/#search=closest
2021-03-22 16:15:02
仍不受当前版本的 Internet Explorer、Edge 和 Opera Mini 支持。
2021-03-24 16:15:02

这可以解决问题:

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

while 循环一直等到el拥有所需的类,并且每次迭代都会将其设置elel的父级,因此最后,您拥有该类的祖先或null.

这是一个小提琴,如果有人想改进它。它不适用于旧浏览器(即 IE);请参阅classList 的兼容性表parentElement在这里使用是因为parentNode需要更多的工作来确保节点是一个元素。

@FelixKling:刚刚编辑,现在可以在不存在这样的祖先的情况下正常工作。我一定对简短的原始版本过于渴望。
2021-03-13 16:15:02
如果要检查参数元素本身中的类名,则在搜索其祖先之前,只需切换循环中条件的顺序即可: while (!el.classList.contains(cls) && (el = el.parentElement));
2021-03-17 16:15:02
呃,我以为它不存在,但我错了无论如何,它parentElement是一个相当新的属性(DOM 级别 4)并且parentNode存在于......永远。所以parentNode也适用于较旧的浏览器,而parentElement可能不会。当然,您可以对 /against 提出相同的论点classList,但它没有像parentElementhas那样简单的替代方案我实际上想知道为什么parentElement存在,它似乎没有增加任何valueparentNode
2021-03-21 16:15:02
我修复了代码,但如果没有具有这样类名的祖先,它仍然会抛出错误。
2021-03-22 16:15:02
有关 的替代方法.classList,请参阅stackoverflow.com/q/5898656/218196
2021-03-26 16:15:02

使用element.closest()

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

请参阅此示例 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>

这是您使用 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
@ShashwatKumar 好问题兄弟。
2021-03-16 16:15:02
对于最接近的 div 而不是它本身呢?
2021-03-26 16:15:02
我看到除 Internet Explorer 之外的所有浏览器现在都支持“最近”(2020 年 6 月)。
2021-04-11 16:15:02

基于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 解决方案运行良好,但如评论中所述ParentElementClassList两者都存在兼容性问题。为了使其更兼容,我使用了:

function findAncestor (el, cls) {
    while ((el = el.parentNode) && el.className.indexOf(cls) < 0);
    return el;
}
  • parentNode财产而不是parentElement财产
  • indexOf方法的className属性而不是contains在方法classList属性。

当然, indexOf 只是在寻找该字符串是否存在,它并不关心它是否是整个字符串。因此,如果您有另一个具有“祖先类型”类的元素,它仍然会返回找到“祖先”,如果这对您来说是个问题,也许您可​​以使用正则表达式来查找精确匹配项。