测试节点是否有任何与给定选择器匹配的子节点的现代、简洁和快速的方法是什么?
“简洁”是指类似于 jQuery 或函数式风格的东西,例如避免循环。我知道原生选择器越来越多地使用这种类型的东西,但没有跟上发展的步伐。如果跨浏览器尚不存在这种情况,那么我也想知道。
我希望它很简单,但搜索 Google 和 SO 会发现许多使用 jQuery 的错误命中或在任何深度找到任意后代,而不仅仅是直接子代。在浏览器之间添加和标准化许多函数式方法之前,还有一些过时的问题。
测试节点是否有任何与给定选择器匹配的子节点的现代、简洁和快速的方法是什么?
“简洁”是指类似于 jQuery 或函数式风格的东西,例如避免循环。我知道原生选择器越来越多地使用这种类型的东西,但没有跟上发展的步伐。如果跨浏览器尚不存在这种情况,那么我也想知道。
我希望它很简单,但搜索 Google 和 SO 会发现许多使用 jQuery 的错误命中或在任何深度找到任意后代,而不仅仅是直接子代。在浏览器之间添加和标准化许多函数式方法之前,还有一些过时的问题。
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>
具有更广泛浏览器支持的解决方案:
[].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")
使用子选择器>
document.querySelectorAll('.parent-selector > .child-selector').length > 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>…</p>, etc]