(请不要使用JQUERY) 假设我有以下树。我想迭代从
querySelectorAll('div.bar')
开始匹配 div.foo
的元素,但不迭代匹配相同选择器的子元素。我该怎么做?
div.foo
div.bar -- select
div.bar -- select
div.bar -- skip
div.bar -- skip
div.damnSon
div.bar -- select
div.bar -- select
div.bar --skip
div.bar -- select
div.bar -- skip
TreeWalker
是不行的,因为
NodeIterator
也不起作用,即使您拒绝父级,它仍然会迭代子级。最理想的情况是以某种方式获得不平坦的
querySelector
结果。然后我只需循环它们而不必关心嵌套属性。
var elems=Array.prototype.filter.call(querySelectorAll('div.bar'),function(el){
var foochild=false;
while(el=el.parent){
if(el.classList.contains("bar") return false;//fail as its a div.bar children
if(el.classList.contains("foo")) foochild=true;//were a children of foo
}
return foochild;
});
只需在 dom 树中过滤有关其父母的 div.bars 即可。
另一种方法是迭代所有直接 foo 子代:
Array.prototype.forEach.call(document.querySelectorAll("div.foo"),function iterate(foo){
for(var i=0;i<foo.children.length){
var child=foo.children[i];
if(child.classList.contains("bar")){
//weve found a bar
}else{
iterate(child);//find subbars
}
}
});
到目前为止我想出了以下戒酒方法:
let nodeIterator = document.createNodeIterator(root, NodeFilter.SHOW_ELEMENT);
let node;
while(node = nodeIterator.nextNode()){
if (node.matches('.bar .bar')) {
//skip
} else if (node.matches('.bar')) {
//do things
}
}
这样做的问题是:
div.foo
(即我的根元素)本身嵌套在 (div.bar
)如果有人可以建议更好的方法,我们将不胜感激。
更新: 好吧,所以我想出了以下方法,这解决了最后一点,但效率又不那么低:
let bars = root.getElementsByClassName('bar');
let excludedChildren = [];
for(let i = 0; i < bars.length; i++){
let children = bars[i].getElementsByClassName('bar');
if(children.length>0) Array.prototype.push.apply(excludedChildren, children);
}
bars = bars.filter(e=>!excludedChildren.includes(e));
bars.forEach(e=>{
//do stuff
});
更新: 是时候结束这个旧事了,这是我的最终解决方案。目前已投入生产一个月,没有出现任何问题。当然,这无法处理一些非常复杂的情况,但它是可扩展的。
您可以在 CSS 选择器中使用
:not
排除嵌套元素,例如:
querySelectorAll('div.bar:not(div.bar div.bar)')
在 codepen 上查看它或运行代码片段:
let foo = document.querySelector("div.foo");
let bars = foo.querySelectorAll("div.bar:not(div.bar div.bar)");
bars.forEach((div) => {
div.style.border = "3px solid black";
div.style.backgroundColor = "yellow";
});
div {
padding-left: 1em;
margin-bottom: 3px;
}
.foo {
width: 200px;
}
.bar::before {
content: "bar";
}
<div class="foo">
<div class="bar"></div>
<div class="bar">
<div class="bar"></div>
<div class="bar"></div>
</div>
<div class="damnSon">
<div class="bar"></div>
<div class="bar">
<div class="bar"></div>
</div>
</div>
<div class="bar">
<div class="bar"></div>
</div>
</div>