web-dev-qa-db-ja.com

特定のクラスを持つ最も近い先祖要素を見つめる

特定のクラスを持つツリーの一番上にある要素の先祖を、純粋なJavaScriptでどのように見つけることができますか?たとえば、次のようなツリーでは、

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

それでpでこれを試してancestorを検索するならdiv.near.ancestorが欲しいです。

164
rvighne

更新:ほとんどの主要ブラウザでサポートされるようになりました

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;
}
244
the8472

これがトリックです:

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

Whileループは、elが目的のクラスになるまで待機し、繰り返しのたびにelelの親に設定するので、最終的にはそのクラスの祖先またはnullが得られます。

誰かがそれを改良したいのであれば、これが のフィドルです。古いブラウザ(IEなど)では機能しません。 classList の互換性表を参照してください。 parentElementは、ノードが要素であることを確認するためにより多くの作業を必要とするため、ここではparentNodeが使用されています。

181
rvighne

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
24
simbro

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;
}
11
marverix

@rvighneソリューションはうまく機能しますが、コメントParentElementClassListには互換性の問題があります。それをより互換性のあるものにするために、私は使いました:

function findAncestor (el, cls) {
    while ((el = el.parentNode) && el.className.indexOf(cls) < 0);
    return el;
}
  • parentNodeプロパティの代わりにparentElementプロパティ
  • indexOfプロパティのclassNameメソッドの代わりにcontainsプロパティのclassListメソッド。

もちろん、indexOfは単にその文字列の存在を探しているだけなので、それが文字列全体であるかどうかは関係ありません。したがって、クラス 'ancestor-type'を持つ別の要素があっても、それが 'ancestor'を見つけたものとして返されることになります。これが問題であれば、おそらくregexpを使って完全に一致するものを見つけることができます。

9
Josh