childNodes

前言

今天有同学向我问了在killCube中遇到的BUG。

最终的原因是有关Node.childNodes的使用不当造成的。

本文做些记录。

重现BUG

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<main id="main">
<div id="con">
<!-- <div class="row">
<div class="cell"></div>
<div class="cell black"></div>
<div class="cell"></div>
<div class="cell"></div>
</div>
<div class="row">
<div class="cell"></div>
<div class="cell"></div>
<div class="cell black"></div>
<div class="cell"></div>
</div>
<div class="row">
<div class="cell black"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
</div>
<div class="row">
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell black"></div>
</div> -->
</div>
</main>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function move( ) {
var con=$('con');
var top=parseInt(window.getComputedStyle(con,null)['top']);
if(speed+top>0)
{top=0;}
else {
top+=speed;
}
con.style.top=top+"px";
if (top==0)
{
creatrow();
con.style.top='-100px';
delrow();
} else if(top==(-100 + speed )){
var rows=con.childNodes;
if((rows.length==5)&&(rows[rows.length-1].pass!==1)) {
fail();
}
}
}

在以上代码中,move()函数中其中一部分逻辑是指 <div id="con">的子节点长度为5时,且另一个逻辑rows[rows.length-1].pass!==1为真时,调用fail()函数,游戏结束。

可这位同学以上的代码,却不能让游戏正常结束。

问题就出现在那段注释上,会导致con.childNodes.length在游戏的一开始就40+,远远超过了5,所以游戏不会结束。


具体分析

分有注释和没有注释的两种情况进行讨论

没有注释

1
<div id="con"></div>
1
2
3
4
5
6
7
function $(id) {
return document.getElementById(id)
}
var con =$("con");
var rows=con.childNodes;
console.log(rows.length);
console.log(rows);

0.png

以上结果很容易理解,con节点并没有子节点

现在将HTML结构进行一些改变

1
2
3
4
 <div id="con"> </div>
### 或者
<div id="con">
</div>

1.png

这样就有了一个子节点了。
因为childNode将(空格,tab,换行等也计算了进去)

2.png
当然具体到空格还是回车换行等具体节点,它们的data属性还是不同,这里不做介绍。

引入注释

1
2
3
 <div id="con">
<!-- <div></div> -->
</div>

3.png

这种情况下,con节点有了三个子节点,除了两处的换行,注释也成为了一个节点


问题总结

回到最初的问题我们可以得出结论

这位同学在HTML引入了空白以及换行符,导致子节点的个数远远增加,而不是预期的0,导致不能满足游戏结束的条件。而killCube中,子节点是通过脚本动态写入的,没有空格回车注释等,故不存在上述问题。

有的时候,bug往往是因为HTML、CSS没写好造成的,导致需要写更多JS去维护。

childNodes与children

Node.childNodes返回包含 指定节点的子节点的集合 ,该集合为即时更新的集合(live collection)

ParentNode.children是一个只读属性,返回 一个 Node子elements活 HTMLCollection

ParentNode.children只返回 元素 的集合,因此使用ParentNode.children可以避免之前的问题出现

有关nodeType可以看看这儿。

封装

ParentNode.children只适用于nodeType为1的节点。

我稍微封装了一下,可以根据自己的需求使用

1
2
3
4
5
6
let  getXNodes = (parentNode) => {
let arrayNodes = Array.from(parentNode.childNodes),
filterResult = arrayNodes.filter((item,index,array) => {
return (item.nodeName !== "#text")
});
};

参考

js子节点children和childnodes的用法

关注我的微信公众号[李一二],即时看更多的文章