web-dev-qa-db-ja.com

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

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

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

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

問題は、myDiv要素でクエリを実行しているため、セレクタに#myDivを含める必要があることです(したがって明らかに冗長です)。

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

セレクタが実行されている要素の直接の子だけを取得するセレクタを記述する方法を知っている人はいますか?

87
mattsh

良い質問。それが尋ねられたとき、「コンビネーターに根ざしたクエリ」を行うための普遍的に実装された方法( John Resigがそれらを呼んだ )は存在しませんでした。

:scope 擬似クラスが導入されました。 EdgeまたはIEの[Pre-Chrominum]バージョンでは supported ではありませんが、Safariで既に数年間サポートされています。それを使用すると、コードは次のようになります。

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

_

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

そうでなければ、まだ_:scope_に依存できない場合、カスタムフィルターロジックを追加せずに状況を処理する別の方法を考えることはできません(たとえば、_.parentNode === myDiv_のmyDiv.getElementsByClassName("foo")を見つけます)、任意のセレクター文字列を入力として、一致のリストを出力として取得したいだけのコードパスをサポートしようとしている場合、明らかに理想的ではありません。しかし、私のように「あなたが持っていたのはハンマーだけだった」と思って立ち往生したという理由だけでこの質問をした場合、otherDOMが提供するツール。

45
natevw

セレクタが実行されている要素の直接の子だけを取得するセレクタを記述する方法を知っている人はいますか?

現在の要素に「ルート化」されているセレクタを記述する正しい方法は、 :scope

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

ただし、ブラウザのサポートは制限されています 使用する場合はシムが必要です。この目的で scopedQuerySelectorShim を作成しました。

111
lazd

Vanilla 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;
}
4
csuwldcat

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

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

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

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はセレクターです。完全にプロトタイプとしても使用できます。

2
Randy Hall

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

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

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!

1
Melle

この状況を処理する関数を作成し、共有すると思いました。

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

本質的にあなたがしていることは、ランダム文字列を生成することです(ここでのrandomString関数はインポートされたnpmモジュールですが、独自のものを作成できます)。その後、>を自由に使用できます。

Id属性を使用していないのは、id属性が既に使用されている可能性があり、オーバーライドしたくないためです。

1
cpi

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
Dustin Poissant

現在のノードに一時的な属性を割り当てるだけで: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");