Javascript DOM:迭代元素,不包括匹配同一选择器的嵌套元素

Javascript DOM:迭代元素,不包括匹配同一选择器的嵌套元素,javascript,dom,css-selectors,Javascript,Dom,Css Selectors,(请不要使用JQUERY) 假设我有下面的树。我想从div.foo开始迭代匹配querySelectorAll('div.bar')的元素,但不迭代匹配同一选择器的子元素。我该怎么做 div.foo div.bar -- select div.bar -- select div.bar -- skip div.bar -- skip div.damnSon div.bar -- select div.bar -- select div.b

(请不要使用JQUERY) 假设我有下面的树。我想从
div.foo
开始迭代匹配
querySelectorAll('div.bar')
的元素,但不迭代匹配同一选择器的子元素。我该怎么做

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
的结果,该结果不是平坦的。然后,我只需循环它们,而不必关心嵌套属性。

到目前为止,我已经提出了以下方法:

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
  }
}
这方面的问题是:

  • 它很慢-特别是在SVG或任何有很多元素的东西中
  • 还是平的
  • 如果
    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
});
let bar=root.getElementsByClassName('bar');
let excludedChildren=[];
for(设i=0;i0)Array.prototype.push.apply(不包括孩子,孩子);
}
bars=bars.filter(e=>!excludedChildren.includes(e));
bars.forEach(e=>{
//做事
});

更新: 是时候把这个老房子关起来了。已经在生产中工作了一个月,没有任何问题。当然,这将无法处理一些非常复杂的情况,但它是可扩展的

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.bar


另一种方法是遍历所有direct foo Child:

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
   }
  }
});
Array.prototype.forEach.call(document.querySelectorAll(“div.foo”),函数迭代(foo){

对于(var i=0;i检查类是否在父链元素中…@Jonasw,这将不起作用。首先,您要求对每个匹配的元素在树上执行子迭代,查看它是否可能包含父元素,然后在调用
parent.containsNode
时执行另一次迭代。第二,如果我从根元素调用它元素使用相同的选择器所有元素都不会通过检查,除非我显式地停止根元素上的循环,这使得它更加低效。我看不到纯CSS选择器在这里有多大帮助,因为不存在“父选择器”,并且因为
:not()
只接受简单的选择器,不适合表示“如果不在”更复杂的树中的星座//你能更详细地说明你最终想用这些元素做什么吗?在我看来,你的自我回答现在指出的三个问题中的两个应该已经在问题本身中被表述为限制。那么“还有什么?”有没有…?说到CSS选择器,我不完全确定我是否正确解释了嵌套的DOM结构,也不确定实际满足了多少需求,这就是相对简单的CSS可以让你达到的程度:…但这只是为了视觉效果和示例目的应用了一些边界,它可能不会为您提供所需的“非平坦”结果在任何上下文中进行迭代,在该上下文中,您将使用CSS选择器构建JS数据结构以进行进一步处理。如果您希望重建整个树,同时简单地丢弃与选择器.bar.bar匹配的元素,则必须手动执行该操作。选择器匹配的元素集在设计上是平坦的-因为它是一个集合。这甚至比我的解决方案还要慢,如果
div.foo
(即我的根目录),它将不起作用它本身嵌套在一个
div.bar
中。事实上,现在我想它也不会嵌套。需要将它添加到答案中。@megakoresh它真的慢吗?我认为它取决于节点/div.bar关系…您更新的方法依赖于
foo.children
,它只包含直接子级。这将排除
div.bar
s在
div.damnSon
中。