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


225

純粋なJavaScriptで、特定のクラスを持つツリーに最も近い要素の祖先を見つけるにはどうすればよいですか?たとえば、次のようなツリーでは:

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

そして、私が欲しいdiv.near.ancestor、私が上でこれをしようpとするために検索しますancestor


1
外側のdivは親ではなく、要素の祖先であることに注意してくださいp。実際に親ノードのみを取得したい場合は、を実行できますele.parentNode
Felix Kling

@FelixKling:その用語を知りませんでした。変更します。
rvighne 2014年

3
それは実際に私たち人間が使用するものと同じです:)あなたの父親(親)の父親(親)はあなたの父親(親)ではなく、祖父(祖父母)、またはより一般的に言えばあなたの祖先です。
フェリックスクリング2014年

回答:


346

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

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

1
現在のバージョンのInternet Explorer、Edge、Opera Miniではまだサポートされていません。
kleinfreund 2015年

1
@kleinfreund-IE、Edge、またはOpera miniではまだサポートされていません。caniuse.com/#search=closest
evolutionxbox

8
2016年6月:すべてのブラウザーがまだ追いついていないようです
Kunal

3
あるDOMレベル4とポリフィルclosestプラス多くの、より高度なDOMのトラバーサル機能が。その実装をそれ自体でプルするか、バンドル全体を含めて、コードに多くの新機能を組み込むことができます。
Garbee、

2
Edge 15はすべての主要なブラウザをカバーするサポートを持っています。IE11を数えない限り、それは何の更新も受け取りません
the8472

195

これはトリックを行います:

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

whileループelは、希望するクラスが存在するまで待機し、反復ごとにの親に設定elするelため、最終的には、そのクラスの祖先またはを使用できnullます。

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


1
の代替案については、stackoverflow.com / q / 5898656/218196を.classList参照してください。
Felix Kling

1
コードを修正しましたが、そのようなクラス名を持つ祖先がない場合でもエラーがスローされます。
Felix Kling 2014年

2
ああ、存在しないと思いましたが、間違いでした。とにかく、parentElementはかなり新しいプロパティ(DOMレベル4)でparentNodeあり、...ずっと存在しています。したがってparentNode、古いブラウザでも機能しますが、機能しparentElementない場合があります。もちろん、/に対して同じ議論をすることもできますがclassListparentElementhasのような単純な選択肢はありません。なぜparentElement存在するのか、実際には何の価値もないように思えますparentNode
Felix Kling 2014年

1
@FelixKling:編集したばかりで、そのような祖先が存在しない場合でも問題なく機能するようになりました。短いオリジナル版に熱中しているに違いない。
rvighne 2014年

3
あなたはその祖先を検索する前に、パラメータ要素自体にクラス名をチェックしたい場合は、単にループ内の条件の順序を切り替えることができますwhile (!el.classList.contains(cls) && (el = el.parentElement));
NicoMak社

34

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

14

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

@rvighneソリューションはうまく機能しますが、コメントで特定されているようにParentElementClassList両方に互換性の問題があります。より互換性を持たせるために、私は以下を使用しました:

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.