对于childNodes的认识

前言

对于原生dom操作熟悉的童鞋都会知道childNodes是用来获取某个元素的子元素集合的,返回值是一个对象数组,但一些操作所带来的结果有时候并不是你所预料的一样。

Text返回值

1
2
3
4
5
6
7
8
<div id = 'wrap'>
<div class='test'>test</div>
</div>
<script>
var _wrap = document.getElementById('wrap'),
nodeList = _wrap.childNodes;
console.log(nodeList.length);
</script>

很多人的第一反应就是,应该是1,放在浏览器一运行,发现是3。是不是有点大跌眼镜的感觉,打开nodeList对象数组一看,发现除了div.test之外,还多了两个text对象,这两个又是什么gui呢?打开第一个Text对象发现,它的父节点是#wrap,它的下一个兄弟节点是.test,而它的内容是’’,好吧,其实就是.test前面的空格,同理,另外一个Text对象就是.test后面的空格。所以如果按照下面的写法,childNodes自然就为1:

1
2
3
4
5
6
<div id = 'wrap'><div class='test'>test</div></div>
<script>
var _wrap = document.getElementById('wrap'),
nodeList = _wrap.childNodes;
console.log(nodeList.length); //1
</script>

那么,如果#wrap没有节点的时候,会返回什么呢?这里同样有两种写法:

1
2
3
4
5
6
7
<div id = 'wrap'>
</div>
<script>
var _wrap = document.getElementById('wrap'),
nodeList = _wrap.childNodes;
console.log(nodeList.length);
</script>

自然,这样的话也同样会返回Text对象,但只有一个,所以length为1.

1
2
3
4
5
6
<div id = 'wrap'></div>
<script>
var _wrap = document.getElementById('wrap'),
nodeList = _wrap.childNodes;
console.log(nodeList.length);
</script>

这样的话,才会返回我们所想象的空数组。最后一种情况,当#wrap中有多个div对象的时候,又会出现什么情况呢?首先是比较简单的没空格情况:

1
2
3
4
5
6
<div id = 'wrap'><div class='test'>test</div><div class='test'>test</div><div class='test'>test</div></div>
<script>
var _wrap = document.getElementById('wrap'),
nodeList = _wrap.childNodes;
console.log(nodeList.length); //3
</script>

这样的话,显然是有3个节点,但如果换成我们书写习惯缩进的情况呢?

1
2
3
4
5
6
7
8
9
10
11
<div id = 'wrap'>
<div class='test'>test</div>
<div class='test'>test</div>
<div class='test'>test</div>
<div class='test'>test</div>
</div>
<script>
var _wrap = document.getElementById('wrap'),
nodeList = _wrap.childNodes;
console.log(nodeList.length);
</script>

发现length输出为9,除了4个.test元素节点以外有5个Text节点,为#wrap后面的空格,4个.test后面的空格。

appendChild操作

那么,append操作插入的节点又是什么样的情况呢?

1
2
3
4
5
6
7
8
9
10
11
<div id="wrap"></div>
<script>
var wrap = document.getElementById('wrap'),newDiv;
for(var i = 0 ; i < 4;i++){
newDiv = document.createElement('div');
newDiv.innerHTML = 'test';
wrap.appendChild(newDiv);
}
var nodeList = wrap.childNodes;
console.dir(nodeList);
</script>

在4次for循环里都创建一个div元素,然后插入到#wrap,输出length一看,发现确实就是4,说明appendChild操作产生的是没有空格形式存在的,这点要注意。

removeChild操作

其实,正如数组是一种引用类型一样,对于childNodes的相关操作都会影响到引用值的变化,这一点如果不记清楚的话很容易造成一些意想不到的错误。如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
<div id="wrap"></div>
<script>
var wrap = document.getElementById('wrap'),newDiv;
for(var i = 0 ; i < 4;i++){
newDiv = document.createElement('div');
newDiv.innerHTML = i;
wrap.appendChild(newDiv);
}
//删除第二个元素
var nodeList = wrap.childNodes;
wrap.removeChild(nodeList[1]);
console.dir(nodeList);
</script>

在一开始的时候循环创建了4个div元素,然后将第二个元素给删除了,这个时候需要注意了,其实nodeList也是随之变化的,正如前面所说,nodeList是一个对象数组引用值,当数组的实际情况发生改变时,那么,nodeList这个引用值也会发生改变。当删除掉第二个元素的时候,nodeList的length其实已经变成了3,这个时候如果你再去访问nodeList[3]是会出错的,并且,这个时候的nodeList[1]是存在,打印它的innerHTML发现是2,所以发现删除了第二个元素的时候,第三个元素就会往前推,变成nodeList[1],这一点也要注意。

结语

上面发现,对于dom的原生操作还是有许多意想不到的情况出现的,这时候就要多打印几次出来看看里面的对象,一不留神,可能就忽略了一些容易忽略的地方从而造成很难察觉的错误。