JavaScript - 从数组中删除子元素或父元素

3

我得到了一段代码,可以从一个可能包含子元素和父元素的随机数组中移除它们,例如:

<html>

    <body>
        <div id='1'>
            <div id='2'>
                <div id='3'>
                </div>
                <div id='4'>
                </div>
            </div>
        </div>
        <div id='5'>
            <div id='6'>
            </div>
        </div>
    </body>

</html>

arr = document.getElementsByTagName('div')
// arr: [<div#1>,<div#2>, <div#3>, <div#4>, <div#5>, <div#6>]

那么从这个例子中,我该如何提取子元素:

// arr: [<div#3>, <div#4><div#6>]

或者提取父元素:
// arr: [<div#1>, <div#5>]

目前我正在使用:

function isDescendant(parent, child) {
     var node = child.parentNode;
     while (node != null) {
         if (node == parent) {
             return true;
         }
         node = node.parentNode;
     }
     return false;
}

function filterArray(arr, parent=true){
    newArr = [];
    arr.forEach((a)=>{
        bool = true

        if (parent){
            arr.forEach((b)=>{
                if (isDescendant(a, b)){
                    bool = false
                };
            });
        }
        else{
            arr.forEach((b)=>{
                if (isDescendant(b, a)){
                    bool = false
                };
            });            
        }

        if(bool){
            newArr.push(a)
        }
    });
    return newArr
};

但我相信一定有更好的解决方案,更加高效。你有什么更好的想法吗?


使用选择器 div > div 可以立即摆脱很多父元素。 - charlietfl
parent.contains(child) is another way of doing isDescendant(parent, child) - jspcal
谢谢 @jspcal,很高兴知道这个 :) - Captain_Meow_Meow
3个回答

1

数组有一个叫做filter的方法,可以让你过滤数组。要查找节点是否是另一个节点的父节点或子节点,可以使用contains方法(请注意,当检查节点是否包含自身时,可能会返回true),或者更通用的compareDocumentPosition方法。

const nodes = Array.from(document.body.querySelectorAll("div"));

//The most straight-forward way to find the parents, 
//filter out any nodes where no other node in the array contains it 
//(note the m !== n check, which prevents contains to return true for the same node):
let parents = nodes.filter( n => !nodes.find( m => m !== n && m.contains(n) ));
//Conversely, to find any child-nodes, invert the contains-check to find any nodes that does not contain any other node in the array:
let children = nodes.filter( n => !nodes.find( m => m !== n && n.contains(m) ));
console.log("approach 1:\n", parents, "\n", children);

//Here is the same approach using compareDocumentPosition instead of contains:
parents = nodes.filter( n => !nodes.find(m => m.compareDocumentPosition(n) & Node.DOCUMENT_POSITION_CONTAINED_BY) );
children = nodes.filter( n => !nodes.find(m => n.compareDocumentPosition(m) & Node.DOCUMENT_POSITION_CONTAINED_BY) )

console.log("approach 2:\n", parents, "\n", children);

//And finally, if you don't need the restriction of checking against 
//elements in the array, you can just see if the nodes have 
//the topmost parent/any children at all:
const topElement = document.body;
parents = nodes.filter( n => n.parentElement === topElement );
children = nodes.filter( n => !n.childElementCount );
console.log("approach 3:\n", parents, "\n", children);
<div id='1'>
  <div id='2'>
    <div id='3'>
    </div>
    <div id='4'>
    </div>
  </div>
</div>
<div id='5'>
  <div id='6'>
  </div>
</div>

一个快速的基准测试显示最后一种方法是最快的(至少在我的机器上)(毫不奇怪,它不必多次搜索数组),其次是“contains”版本。最慢的是使用“compareDocumentPosition”,但仍然比运行“filterArray”来获取子数组要快。

0
尝试以下方法。虽然不是通用的,但它可以选择正确的答案。希望对您有用。在场景下应该使用较少的数据循环。
const nodes = Array.from(document.body.querySelectorAll("div"));

const children = nodes.filter( node => 0 === node.querySelectorAll('div').length );
const parents = nodes.filter( node => 'DIV' !== node.parentNode.nodeName );

这是 JsFiddle: https://jsfiddle.net/3nhzvat9/ 还有另一个解决方案,应该表现得更好:
const nodes = document.body.querySelectorAll("div");
const children = [];
const parents = [];

nodes.forEach( node => {
   if(0  === node.querySelectorAll('div').length) {
      children.push(node);
   }
   if('DIV' !== node.parentNode.nodeName){
     parents.push(node);
   }
});

第二个 JsFiddle:https://jsfiddle.net/3nhzvat9/1/


0

对于孩子们,可以将集合转换为数组并使用querySelector()进行filter(),当元素不是父级时不会返回任何内容

对于外部父级,可以使用Array#some()node#contains()作为filter()

const arr = Array.from(document.querySelectorAll('div'));

const innerChildren = arr.filter(el => !el.querySelector('*')); // or use same selector as master query

const outerParents = arr.filter(e => !arr.some(el => el !== e && el.contains(e)));

console.log(innerChildren) // div#3, div#4 & div#6  
console.log(outerParents) //  div#1, div#5
<div id='1'>
  <div id='2'>
    <div id='3'></div>
    <div id='4'></div>
  </div>
</div>
<div id='5'>
  <div id='6'></div>
</div>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接