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

Posted

技术标签:

【中文标题】查找具有特定类的最近的祖先元素【英文标题】:Find the closest ancestor element that has a specific class 【发布时间】:2014-04-02 21:39:00 【问题描述】:

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

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

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

【问题讨论】:

请注意,外部 div 不是父级,它是p 元素的祖先。如果你真的只想获取父节点,可以ele.parentNode @FelixKling:不知道这个术语;我会改的。 其实和我们人类用的一样 :) 你父亲(父母)的父亲(父母)不是你的父亲(父母),而是你的祖父(祖父母),或者更通俗地说,你的祖先。 【参考方案1】:

更新:现在支持in most major browsers

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;

【讨论】:

当前版本的 Internet Explorer、Edge 和 Opera Mini 仍然不支持。 @kleinfreund - 在 IE、Edge 或 Opera mini 中仍然不受支持。 caniuse.com/#search=closest 2016 年 6 月:看起来所有浏览器都没有赶上 有一个带有closest 的DOM Level 4 polyfill 以及许多更高级的DOM 遍历功能。您可以单独提取该实现或包含整个捆绑包以在您的代码中获得许多新功能。 Edge 15 支持,应该涵盖所有主流浏览器。除非你算上 IE11,但它不会收到任何更新【参考方案2】:

这就是诀窍:

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

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

Here's a fiddle,如果有人想改进它。它不适用于旧浏览器(即 IE);看到这个compatibility table for classList。此处使用parentElement 是因为parentNode 将涉及更多工作以确保该节点是一个元素。

【讨论】:

有关.classList 的替代方案,请参阅***.com/q/5898656/218196。 我修复了代码,但如果没有具有此类名称的祖先,它仍然会抛出错误。 呃,我以为它不存在,but I was wrong。无论如何,parentElement 是一个相当新的属性(DOM 级别 4),parentNode 存在于......永远。所以parentNode 也适用于旧版浏览器,而parentElement 可能不行。当然,您可以为/反对classList 提出相同的论点,但它没有像parentElement 那样的简单替代方案。我真的很想知道为什么parentElement 存在,它似乎没有比parentNode 增加任何价值。 @FelixKling:刚刚编辑,它现在可以很好地适用于不存在这样的祖先的情况。我一定是对简短的原始版本过于渴望了。 如果你想在参数元素本身中检查类名,在搜索它的祖先之前,你可以简单地在循环中切换条件的顺序:while (!el.classList.contains(cls) &amp;&amp; (el = el.parentElement));【参考方案3】:

使用 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

【讨论】:

我看到除了 Internet Explorer 之外的所有浏览器现在都支持“最近”(2020 年 6 月)。 最近的 div 而不是它自己呢? @ShashwatKumar 兄弟问得好。【参考方案4】:

基于the8472 answer 和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;

【讨论】:

【参考方案5】:

@rvighne 解决方案效果很好,但正如 cmets ParentElementClassList 中所确定的那样,两者都存在兼容性问题。为了使其更兼容,我使用了:

function findAncestor (el, cls) 
    while ((el = el.parentNode) && el.className.indexOf(cls) < 0);
    return el;

parentNode 属性而不是 parentElement 属性 className 属性上的indexOf 方法,而不是classList 属性上的contains 方法。

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

【讨论】:

【参考方案6】:

此解决方案应该适用于 IE9 及更高版本。

这就像 jQuery 的 parents() 方法,当您需要获取可能比给定元素高几级的父容器时,例如找到包含单击的 &lt;button&gt;&lt;form&gt;。遍历父母直到找到匹配的选择器,或者直到它到达&lt;body&gt;。返回匹配元素或&lt;body&gt;

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;

【讨论】:

以上是关于查找具有特定类的最近的祖先元素的主要内容,如果未能解决你的问题,请参考以下文章

jQuery中最近的祖先节点

最近公共祖先 LCA 递归非递归

9018:1892:最近公共祖先

递归加入最近的祖先/父母记录在同一个表中?

最近公共祖先三种类型汇总(漫画版)

使用绝对路径查找祖先