使用querySelectorAll检索直接子项

时间:2010-09-09 21:57:27

标签: javascript dom css-selectors

我能够做到这一点:

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

也就是说,我可以成功检索myDiv元素中具有类.foo的所有直接子元素。

问题是,我必须在选择器中包含#myDiv,这让我感到困扰,因为我在myDiv元素上运行查询(所以它显然是多余的)。

我应该能够关闭#myDiv,但选择器不是合法语法,因为它以>开头。

有没有人知道如何编写一个选择器,它只获取运行选择器的元素的直接子元素?

14 个答案:

答案 0 :(得分:106)

  

有没有人知道如何编写一个选择器,它只获取运行选择器的元素的直接子元素?

编写“根”到当前元素的选择器的正确方法是使用:scope

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

然而, browser support is limited如果你想使用它,你需要一个垫片。我为此建立了scopedQuerySelectorShim

答案 1 :(得分:37)

很好的问题,但正如您所见,无法将“组合根源查询”视为John Resig names them

但是,在某些情况下,您可以跳过.querySelectorAll并使用其他良好的老式DOM API功能。例如,您可以只编写myDiv.querySelectorAll("> *"),而不是不受支持的myDiv.children

不幸的是,如果不添加更多自定义过滤器逻辑(例如查找myDiv.getElementsByClassName("foo") .parentNode === myDiv),我无法想办法处理您的情况,如果您尝试支持一个{{1}},那么显然不理想实际上只想将任意选择器字符串作为输入并将匹配列表作为输出的代码路径!但是,如果像我一样,你最终会问这个问题只是因为你一直在思考“你所拥有的只是一把锤子”,不要忘记DOM也提供了各种各样的其他工具。

答案 2 :(得分:4)

这是一个灵活的方法,用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;
}

答案 3 :(得分:2)

如果您确定该元素是唯一的(例如您的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是您的选择器。完全可以用作原型。

答案 4 :(得分:2)

我用这个:

您可以避免使用箭头两次输入“myDiv”。
当然总是有更多的可能性。
可能需要现代浏览器。


<!-- Sample Code -->

<div id="myDiv">
    <div class="foo">foo 1</div>
    <div class="foo">foo 2
        <div class="bar">bar</div>
    </div>
    <div class="foo">foo 3</div>
</div>

// Return HTMLCollection (Matches 3 Elements)

var allMyChildren = document.querySelector("#myDiv").children;

// Return NodeList (Matches 7 Nodes)

var allMyChildren = document.querySelector("#myDiv").childNodes;

// Match All Children With Class Of Foo (Matches 3 Elements)

var myFooChildren = document.querySelector("#myDiv").querySelectorAll(".foo");

// Match Second Child With Class Of Foo (Matches 1 Element)

var mySecondChild = document.querySelector("#myDiv").querySelectorAll(".foo")[1];

// Match All Children With Class Of Bar (Matches 1 Element)

var myBarChild = document.querySelector("#myDiv").querySelector(".bar");

// Match All Elements In "myDiv" (Matches 4 Elements)

var myDescendants = document.querySelector("#myDiv").querySelectorAll("*");

答案 5 :(得分: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;
}

本质上你正在做的是生成一个随机字符串(这里的randomString函数是一个导入的npm模块,但你可以创建自己的。)然后使用该随机字符串来保证你得到你期望的元素。选择。然后您可以在此之后使用>

我没有使用id属性的原因是id属性可能已被使用,我不想覆盖它。

答案 6 :(得分:1)

以下解决方案与目前提出的解决方案不同,对我有用。

理由是您首先选择所有匹配的孩子,然后筛选出不是直接孩子的孩子。如果子项没有具有相同选择器的匹配父项,则子项是直接子项。

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!

答案 7 :(得分:0)

我们可以使用childNodes轻松获取元素的所有直接子元素,并且我们可以选择具有querySelectorAll的特定类的祖先,因此我们很难想象我们可以创建一个新的功能,可以获得两者并比较两者。

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");

答案 8 :(得分: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");

答案 9 :(得分:0)

您可以扩展 Element 以包含这样的方法 getDirectDesc()

Element.prototype.getDirectDesc = function() {
    const descendants = Array.from(this.querySelectorAll('*'));
    const directDescendants = descendants.filter(ele => ele.parentElement === this)
    return directDescendants
}

const parent = document.querySelector('.parent')
const directDescendants = parent.getDirectDesc();

document.querySelector('h1').innerHTML = `Found ${directDescendants.length} direct descendants`
<ol class="parent">
    <li class="b">child 01</li>
    <li class="b">child 02</li>
    <li class="b">child 03 <ol>
            <li class="c">Not directDescendants 01</li>
            <li class="c">Not directDescendants 02</li>
        </ol>
    </li>
    <li class="b">child 04</li>
    <li class="b">child 05</li>
    </ol>
    <h1></h1>

答案 10 :(得分:0)

单线版

var direct_children=Array.from(parent_el.querySelectorAll('span')).filter(function(a){return a.parentNode===parent_el;});

我发现这在给定父元素的情况下非常方便。我对其进行了测试,并且 100% 有效。

答案 11 :(得分:0)

function siblings(el) {
    return el.closest('*:not(:scope)').querySelectorAll(':scope > *');
}

纯JS代码

el - 是你的元素 这里我们有在接下来的步骤中起作用的函数:

  1. 采用任何直接父级,但不采用我们传递的 el(最接近的可以采用 el,因此我们确保 el 不会成为结果)
  2. 带走所有直系子女

答案 12 :(得分:-1)

我只是在没有尝试的情况下这样做。这会有用吗?

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

尝试一下,也许它的工作原理可能没有。 Apolovies,但我现在不在电脑上试试(从我的iPhone回复)。

答案 13 :(得分:-1)

我已经和

一起去了
var myFoo = document.querySelectorAll("#myDiv > .foo");
var myDiv = myFoo.parentNode;