querySelectorAllを使用して直接の子を取得する


119

私はこれを行うことができます:

<div id="myDiv">
   <div class="foo"></div>
</div>
myDiv = getElementById("myDiv");
myDiv.querySelectorAll("#myDiv > .foo");

つまり、myDivclassを持つ要素の直接の子をすべて正常に取得できます.foo

問題は、要素に対して#myDivクエリを実行しているため、セレクターにを含める必要があるmyDivことです。

#myDivオフのままにしておくべきですが、セレクタはで始まるため、正しい構文ではありません>

セレクターが実行されている要素の直接の子だけを取得するセレクターの作成方法を誰かが知っていますか?



どの回答もあなたが必要とするものを達成しませんでしたか?フィードバックを提供するか、回答を選択してください。
ランディホール

@mattsh-必要に応じて、より良い(IMO)回答を追加しました。確認してください!
csuwldcat 2013

回答:


85

良い質問。質問された当時、「John Resigがそれらを呼んでいたように」「combinator rooted query」を実行する普遍的に実装された方法は存在しませんでした。

:スコープ疑似クラスが導入されました。それはされていない、サポートエッジまたはIEの[前Chrominum]バージョンで、すでに数年前からのSafariでサポートされています。これを使用すると、コードは次のようになります。

let myDiv = getElementById("myDiv");
myDiv.querySelectorAll(":scope > .foo");

場合によっては.querySelectorAll、他の古き良きDOM API機能をスキップして使用することもできます。たとえば、代わりに、たとえばとmyDiv.querySelectorAll(":scope > *")書くこともできますmyDiv.children

それ以外の場合:scope、まだ信頼できない場合、カスタムフィルターロジックを追加せずに状況を処理する別の方法(たとえば、who myDiv.getElementsByClassName("foo")を見つける.parentNode === myDiv)を考えることはできません。任意のセレクター文字列を入力として受け取り、一致のリストを出力として受け取りたいだけです!しかし、私のように「ハンマーでした」と思って動けなくなっただけでこの質問をしてしまった場合は、DOM の他にもさまざまなツールがあることを忘れないでください。


3
myDiv.getElementsByClassName("foo")はと同じではありません。子だけではなく、すべての子孫を検索するという点で(実際には実際に機能します)にmyDiv.querySelectorAll("> .foo")myDiv.querySelectorAll(".foo")ています.foo
BoltClock

ああわざわざ!つまり、あなたの言う通りです。OPの場合はあまり良い答えではないことをより明確にするために、回答を更新しました。
natevw 2013年

リンクに感謝します(これは明確に答えているようです)。また、用語「combinator rooted query」を追加してくれてありがとう。
mattsh 2013年

1
John Resigが「combinator-rooted querys」と呼ぶもので、Selectors 4はそれらを相対セレクターと呼びます。
BoltClock

@Andrew Scopedスタイルシート(つまり<style scoped>)は:scope、この回答で説明されている疑似セレクターとは何の関係もありません。
natevw

124

セレクターが実行されている要素の直接の子だけを取得するセレクターの作成方法を誰かが知っていますか?

現在の要素に「ルート」されたセレクターを書き込む正しい方法は、を使用すること:scopeです。

var myDiv = getElementById("myDiv");
var fooEls = myDiv.querySelectorAll(":scope > .foo");

ただし、 ブラウザのサポートには制限があり、使用する場合はシムが必要になります。私が建てscopedQuerySelectorShimを、この目的のために。


3
:scope仕様は現在「ワーキングドラフト」であり、したがって変更される可能性があることに言及する価値があります。それが採用された場合、または採用された場合でも、このように機能する可能性は高いですが、私の意見では、これが「正しい方法」であると言うのは少し早いです。
Zach Lysobey 2015

2
当面は非推奨となったようです
Adrian Moisa

1
3年後、あなたは私の大殿筋を救います!
Mehrad、2018年

5
@エイドリアン・モイサ::scopeはどこでも廃止されていません。スコープ付き属性と混同されている可能性があります。
BoltClock

6

バニラJSで記述された柔軟なメソッドを次に示します。これにより、要素の直接の子に対してのみCSSセレクタークエリを実行できます。

var count = 0;
function queryChildren(element, selector) {
  var id = element.id,
      guid = element.id = id || 'query_children_' + count++,
      attr = '#' + guid + ' > ',
      selector = attr + (selector + '').replace(',', ',' + attr, 'g');
  var result = element.parentNode.querySelectorAll(selector);
  if (!id) element.removeAttribute('id');
  return result;
}

生成するIDに何らかのタイムスタンプまたはカウンターを追加することをお勧めします。がMath.random().toString(36).substr(2, 10)同じトークンを複数回生成する確率はゼロではありません(わずかではあります)。
フレデリックハミディ2013

リピートハッシュのオッズが非常に小さい可能性がどれほどあるかはわかりません。IDはミリ秒のほんの一部だけ一時的に適用されるだけで、重複はすべて同じ親ノードの子である必要があります-基本的に、チャンスは天文学的です。とにかく、カウンターを追加するのは問題ないようです。
csuwldcat 2013

意味がわからない場合any dupe would need to also be a child of the same parent nodeid属性はドキュメント全体に適用されます。確かにオッズはごくわずかですが、高い道路を利用してそのカウンターを追加していただきありがとうございます:)
フレデリックハミディ

8
JavaScriptは並行して実行されないため、実際にはチャンスはゼロです。
WGH 2013

1
@WGHうわー、信じられない、私はそのような単純な点で頭がおかしくなったのに絶対に驚いています(私はMozillaで働いて、この事実を頻繁に唱えます、もっと眠りに就かなければなりません!LOL)
csuwldcat

5

要素が一意であることを確実に知っている場合(IDのケースなど):

myDiv.parentElement.querySelectorAll("#myDiv > .foo");

より「グローバルな」ソリューションの場合:(matchesSelectorシムを使用)

function getDirectChildren(elm, sel){
    var ret = [], i = 0, l = elm.childNodes.length;
    for (var i; i < l; ++i){
        if (elm.childNodes[i].matchesSelector(sel)){
            ret.push(elm.childNodes[i]);
        }
    }
    return ret;
}

どこelmあなたの親要素であり、selあなたの選択です。完全にプロトタイプとしても使用できます。


@lazdは質問の一部ではありません。
ランディホール

あなたの答えは「グローバルな」ソリューションではありません。これには注意すべき特定の制限があるため、私のコメントです。
lazd 2014年

尋ねられた質問に答える@lazd。では、なぜ反対票か。それとも、1年前の回答に対する反対投票から数秒以内にコメントしたのですか?
ランディホール

ソリューションはmatchesSelector接頭辞なし(これは最新バージョンのChromeでも機能しない)を使用し、グローバル名前空間を汚染します(まだ宣言されていません)。querySelectorMethodsが期待するようなNodeListを返しません。私はそれが良い解決策ではないと思うので、反対票を投じます。
lazd 2014年

@lazd-元の質問では、接頭辞なしの関数とグローバル名前空間を使用しています。それは与えられた質問のための完全に良い回避策です。それが良い解決策だと思わない場合は、使用しないか、編集を提案してください。機能的に正しくない場合やスパムの場合は、反対票を検討してください。
ランディホール

2

次の解決策はこれまでに提案されたものとは異なり、私にとってはうまくいきます。

根拠は、最初に一致するすべての子を選択し、次に直接の子ではない子を除外することです。同じセレクターを持つ一致する親がない場合、子は直接の子です。

function queryDirectChildren(parent, selector) {
    const nodes = parent.querySelectorAll(selector);
    const filteredNodes = [].slice.call(nodes).filter(n => 
        n.parentNode.closest(selector) === parent.closest(selector)
    );
    return filteredNodes;
}

HTH!


私は、その簡潔さとアプローチの単純さのためにこれを気に入っていますが、大きなツリーと過度に貪欲なセレクターを使って高い頻度で呼び出された場合は、おそらくうまく機能しません。
brennanyoung 2018

1

共有したいと思って、このような状況を処理する関数を作りました。

getDirectDecendent(elem, selector, all){
    const tempID = randomString(10) //use your randomString function here.
    elem.dataset.tempid = tempID;

    let returnObj;
    if(all)
        returnObj = elem.parentElement.querySelectorAll(`[data-tempid="${tempID}"] > ${selector}`);
    else
        returnObj = elem.parentElement.querySelector(`[data-tempid="${tempID}"] > ${selector}`);

    elem.dataset.tempid = '';
    return returnObj;
}

本質的には、ランダム文字列を生成することです(ここでランダム文字列関数はインポートされたnpmモジュールですが、独自に作成できます)。その後、そのランダム文字列を使用して、セレクターで期待する要素を確実に取得します。その後、その後は自由に使用できます>

id属性を使用していない理由は、id属性がすでに使用されている可能性があり、それを上書きしたくないためです。


0

要素の直接の子をすべて使用して簡単に取得childNodesでき、特定のクラスの祖先をで選択querySelectorAllできるため、両方を取得して2つを比較する新しい関数を作成できると想像するのは難しくありません。

HTMLElement.prototype.queryDirectChildren = function(selector){
  var direct = [].slice.call(this.directNodes || []); // Cast to Array
  var queried = [].slice.call(this.querySelectorAll(selector) || []); // Cast to Array
  var both = [];
  // I choose to loop through the direct children because it is guaranteed to be smaller
  for(var i=0; i<direct.length; i++){
    if(queried.indexOf(direct[i])){
      both.push(direct[i]);
    }
  }
  return both;
}

注:これは、NodeListではなく、ノードの配列を返します。

使用法

 document.getElementById("myDiv").queryDirectChildren(".foo");

0

現在のノードに一時的な属性を割り当てるだけで:scopeの互換性を拡張できることを付け加えておきます。

let node = [...];
let result;

node.setAttribute("foo", "");
result = window.document.querySelectorAll("[foo] > .bar");
// And, of course, you can also use other combinators.
result = window.document.querySelectorAll("[foo] + .bar");
result = window.document.querySelectorAll("[foo] ~ .bar");
node.removeAttribute("foo");

-1

私は行ってきたでしょう

var myFoo = document.querySelectorAll("#myDiv > .foo");
var myDiv = myFoo.parentNode;

-2

やってみないでやってるだけです。これはうまくいくでしょうか?

myDiv = getElementById("myDiv");
myDiv.querySelectorAll(this.id + " > .foo");

試してみてください。うまくいかないかもしれません。Apolovies、しかし私は今コンピュータを使ってそれを試すことはできません(私のiPhoneからの返信)。


3
QSAは、呼び出されている要素にスコープが設定されているため、myDiv内でmyDivと同じIDを持つ別の要素を探し、次にその子を.fooで探します。`document.querySelectorAll( '#' + myDiv.id + '> .foo');のようなことができます。
おそらく14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.