杂七杂八的—— HTMLCollection与NodeList、Element与Node、IndexOf与for

之前用到某个js的的库,库很简单,大概就几百行代码,主要是用来配置另外两个第三方库的。要命的是,这个库依赖jquery,我其它代码里面都没用到jquery,所以也不想为了这个东西而引入如此庞大的jquery。虽然自己重写这个库也是没有关系的,但是我太懒了,不想看这个代码。于是想,要不自己弄一个类似jquery的吧。并不是要重写一个jquery,那我水平还是太渣,我只是想写几个能够让那个库运行起来的方法而已。比如$方法,css方法。但是jquery是自己完全模拟了一套DOM的东西,很多方法都是运行在jquery对象上,所以呢,我就把这些相同的方法都移到DOM上就可以了。嗯,于是遇到一些东西,觉得挺有趣。

一、HTMLCollection与NodeList

这个是为了模仿jquery添加事件发现的问题,

1
2
3
4
// 如果有函数传入,则将这个函数注册到每一个元素
$('#testBtn').click(fn);
// 如果没有参数传入,则触发每个元素的click事件
$('#testBtn').click();

我用了这样的方法

1
2
3
4
5
6
HTMLCollection.prototype.click = function(fn){
var action = fn ? function (ele){ ele.addEventListener('click',fn,false); } : function (ele){ ele.click(); };
for(var i = 0; i < this.length; i++)
action(this.item(i));
return this;
}

首先我说一下,这个吧直接在内部类的原型上定义一些东西是非常不好的行为,大家不要向我学习,这个非常不好。我只是随便研究一下。而且,不可能一个方法就写一次吧,那么多的方法,因为这里方法少所以就这样写了,如果真的要吧所以方法都都实现的话,还是得要再进一步封装啊。而且这里也没做兼容的处理。

于是出现问题了 原本我以为 getElementsBy*这一系列的和 querySelectorAll以及 childNodes这几个返回的都是 HTMLCollection 结果我发现 并不是! 有部分是 NodeList 这是什么情况

后来查了一波 发现确实问题很多 后来查了一下最新的标准 这个是DOM4的标准 我刚刚查的那个标准10月6号才更新过一次 就前三周吧 所以这些标准都是一直在更新的 不能被现在看到了 之后就固定思维下来了 这个标准还有关于这两个类的比较old-style-collections:-nodelist-and-htmlcollection,这个很清楚的说明了两个的区别 : A NodeList object is a collection of nodes. An HTMLCollection object is a collection of elements.

下面就是我最后的结论:

1. getElementsBy*

getElementsBy* 是要返回 HTMLCollection 的(包括getElementsByClassName、getElementsByTagName、getElementsByName) ,这几个方法在DOM3及之前返回的是NodeList 而且getElementsByClassName 这个方法 也是DOM4才加入的, 这几个都是Document的接口,最最重要的是: 这几个方法返回的值是live的!! 虽然好像说这个一直都是live的 但是我现在才知道。。。。。后面加上代码 。

2. querySelectorAll

querySelectorAll 这个是返回NodeList ,但是返回值是static的,就是说不是live的。这个是ParentNode的接口。

3.children与childNodes

children 这个和上面那个一样也都是ParentNode的接口,返回值为 HTMLCollection 。但是 childNodes返回的是NodeList,是Node的接口。这两个返回值都是live的。

然后我做了下面这个测试

1
2
3
4
5
<div id="parent">
<p class="tp">1111</p>
<p class="tp">2222</p>
<p class="tp">3333</p>
</div>
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

var parent = document.getElementById('parent');
var query = {
'querySelectorAll' : '.tp',
'getElementsByClassName' : 'tp',
'getElementsByTagName' : 'p',
'children' : '',
'childNodes' : '',
}
var eles = {};
function getElements (method) {
if (typeof parent[method] == 'function' ){
return parent[method](query[method]);
}else{
return parent[method];
}
}

for(var k in query){
eles[k] = getElements(k);
log(k+'\t' + eles[k].length);
}
addElement(); // 往parent添加一个<p class="tp">444</p>

for(var k in query){
log(k + ': ' + eles[k].length + ' ' + getType(eles[k])); // 使用Object.prototype.toString.call();
}

最后的结果

1.Chrome 45

QQ20151025-0@2x

2.FireFox 41.0.2

QQ20151025-1@2x

3.safari 9.0.1

QQ20151025-2@2x

嘿嘿 傻逼了吧 中出叛徒 safari不对了 它把getElement的返回错了,然而并不是错了 ,而是老的标准返回值为NodeList,只是它没有更新吧可能。剩下两个浏览器都没有问题。
可以看到,只有querySelector不是live的,其它的几个都会随着dom元素的改变而自动改变,貌似是因为在取值的时候回重新计算过。

二、Element与Node

刚刚上面说到NodeList和HTMLCollection的区别的时候说到这个了 Node即是节点,Element即是元素,元素当然属于节点,所以 Element instanceof Node === true; ,所有的html元素都是Element,以及各种继承自Element的元素 ,这个是HTML5的元素。TextNode只是Node,不属于Element。

三、IndexOf与for

后来我随便到处看,随便看看了sizzle.js这个库的源码,发现里面用到一个函数,查找数组中的某个值的位置,原本一般来说是用 indexOf 就可以了的,但是这个库里自己用了一个for循环遍历数组然后查找值。。。。。额。。。。。还说根据测试,for循环速度比原生的要快,测试的地址在这里,thor-indexof-vs-for。我还是有点疑惑,如何然后自己写了一些数据试了一下。。。。果然for快一些。。。。感觉三观毁掉了又。。。。。

这段时候比较空 不知道干些什么好