2

测试节点是否有任何与给定选择器匹配的子节点的现代、简洁和快速的方法是什么?

“简洁”是指类似于 jQuery 或函数式风格的东西,例如避免循环。我知道原生选择器越来越多地使用这种类型的东西,但没有跟上发展的步伐。如果跨浏览器尚不存在这种情况,那么我也想知道。

我希望它很简单,但搜索 Google 和 SO 会发现许多使用 jQuery 的错误命中或在任何深度找到任意后代,而不仅仅是直接子代。在浏览器之间添加和标准化许多函数式方法之前,还有一些过时的问题。

4

4 回答 4

6

一种选择是使用直接子组合子>, 和:scope伪类

var children = parentElement.querySelectorAll(':scope > div');

var parentElement = document.querySelector('.container');
var children = parentElement.querySelectorAll(':scope > div');

for (var i = 0; i < children.length; i++) {
  children[i].style.background = '#f00';
}
.level2 { background-color: #fff; }
<div class="container">
  <span>Span</span>
  <span>Span</span>
  <div class="level1">Direct 'div'
    <div class="level2">Nested 'div'</div>
  </div>
  <div class="level1">Direct 'div'
    <div class="level2">Nested 'div'</div>
  </div>
  <div class="level1">Direct 'div'
    <div class="level2">Nested 'div'</div>
  </div>
</div>

请注意,:scope伪类仍然被认为是实验性的,并且没有完整的浏览器支持。但尽管如此,它可能是最“现代”的解决方案(正如您所要求的)。


或者,您可以使用该.filter()方法并检查父元素的子元素是否与给定的选择器匹配:

function getChildren(parent, selector) {
  return Array.prototype.filter.call(parent.children, function(node) {
    return node.matches(selector);
  });
}

用法:

getChildren(parentElement, 'div'); // Direct children 'div' elements

function getChildren(parent, selector) {
  return Array.prototype.filter.call(parent.children, function(node) {
    return node.matches(selector);
  });
}

var parentElement = document.querySelector('.container');
var children = getChildren(parentElement, 'div');

for (var i = 0; i < children.length; i++) {
  children[i].style.background = '#f00';
}
.level2 { background-color: #fff; }
<div class="container">
  <span>Span</span>
  <span>Span</span>
  <div class="level1">Direct 'div'
    <div class="level2">Nested 'div'</div>
  </div>
  <div class="level1">Direct 'div'
    <div class="level2">Nested 'div'</div>
  </div>
  <div class="level1">Direct 'div'
    <div class="level2">Nested 'div'</div>
  </div>
</div>

于 2016-01-27T02:41:38.097 回答
3

具有更广泛浏览器支持的解决方案:

[].some.call(yourEl.children, function(e){return e.matches(".z")})

在简洁性方面,在 ES2015 中(显然,通过使用转译器)它会更好,带有箭头函数:

[].some.call(yourEl.children, e=>e.matches(".z"))

并使用Array.from(ES2015):

Array.from(yourEl.children).some(e=>e.matches(".z"))

或者,在效用函数中

function childMatches(elmt, selector){
  return [].some.call(elmt.children, function(e){
    return e.matches(selector);
  });
}

用法

childMatches(yourElement, ".any-selector-you-want")

于 2016-01-27T02:47:34.560 回答
1

使用子选择器>

document.querySelectorAll('.parent-selector > .child-selector').length > 0
于 2016-01-27T02:39:12.737 回答
0

如果您想从特定节点开始应用选择器但不能假定:scope支持,您可以像这样为特定节点构建选择器

function selectorPath(node) {
    var idx;
    if (node.nodeName === 'HTML' || !node.parentNode) return node.nodeName;
    idx = Array.prototype.indexOf.call(node.parentNode.children, node);
    return selectorPath(node.parentNode) + ' > ' + node.nodeName + ':nth-child(' + (idx + 1) + ')';
}

然后在多部分选择器中使用它可能看起来像这样

function selectChildAll(parent, selector) {
    var pSelector = selectorPath(parent) + ' > ';
    selector = pSelector + selector.split(/,\s*/).join(', ' + pSelector);
    return parent.querySelectorAll(selector);
}

所以使用它的一个例子可能是,从这个答案的内容中获取所有<p>和直接的孩子,<pre>

var node = document.querySelector('#answer-35028023 .post-text');
selectChildAll(node, 'p, pre'); // [<p>​…​&lt;/p>​, etc​]
于 2016-01-27T02:57:36.350 回答