原生DOM探究 -- NodeList v.s. HTMLCollection
涉及获取元素的主要API
在获取原生DOM元素的时候,主要涉及这几个DOM API(链接为Living Standard):
Node
及对应集合NodeList
Element
(继承Node
)及对应集合HTMLCollection
Document
(继承Node
)
注:计划取代NodeList
和HTMLCollection
的Elements
目前并无广泛实现
基础知识 -- NodeList v.s. HTMLCollection
在不同版本的浏览器中,如果调用获取多元素的DOM方法(getElement...()),有的会得到NodeList
(多为旧浏览器),有的会得到HTMLCollection
(多为新浏览器)。使用Node Interface的方法,如childNodes,得到的通常是NodeList,而使用其他Interface的方法,又有可能得到HTMLCollection
。所以有必要了解一下这两者的区别。
关于这两个类型的差异,在Stackoverflow上有一个不错的问答。
其实,只要先看看Living Standard中这两个类型的IDL,便能猜出大概了。NodeList
的IDL如下:
interface NodeList { getter Node? item(unsigned long index); readonly attribute unsigned long length; iterable<Node>; };
而HTMLCollection
的IDL如下:
interface HTMLCollection { readonly attribute unsigned long length; getter Element? item(unsigned long index); getter Element? namedItem(DOMString name); };
相同点:
- 它们都有
length
属性 - 都有元素的getter,叫做
item
不同点:
-
NodeList
的元素是Node
,HTMLCollection
的元素是Element
。Element
继承自Node
,是Node
的一种,在HTML中,它一般是HTML元素(比如<p>
,<a>
之类的标签创建出来的对象)。而Node
作为父类,除了Element
还有一些其他子类,比如HTML元素内的文本对应的Text
,文档对应的Document
,注释对应的Comment
。HTMLCollection
里,只有Element
,而NodeList
里可以有Element
、Text
、Comment
等多种元素。按说如果获取元素返回的列表里只有Element
,那这两种类没多大区别,但事实上很多时候浏览器会将解析HTML文本时得到的Text
和Comment
一并放进列表里放回。比如说下面这一段代码<div> <!-- Comment --> <p>This is Some Text</p> </div>
若将这个div的子元素放在列表里返回,那么如果是作为
NodeList
返回,浏览器最多可以给这个列表5个元素(不同浏览器可能不同)- 一个
<div>
和注释间的断行和空格(或tab)作为text node(没错,标签之间的空白符号也可以被解析为text node) - 注释作为comment node
- 注释和
<p>
之间的断行和空格(或tab)作为text node p
作为element</p>
和</div>
之间的断行和空格(或tab)作为text node
因此
NodeList
里可能会有很多一般DOM操作不需要的text node和comment node需要处理。而HTMLCollection
则简单多了,只有<p>
这一个元素,这也是比较符合大多数人直觉的结果。 - 一个
-
HTMLCollection
还有一个namedItem
方法,可以快速获取其中元素。假设有这样一段HTML:<div> <!-- Comment --> <p>This is Some Text</p> <img name="test" src="test.jpg"> </div>
那么假设得到了这个div的子元素构成的
HTMLCollection
,叫做list
,那么使用list.namedItem("test")
就可以直接得到里面的img
元素。查找顺序参考Living Standard,但是在现实中不是所有浏览器都遵循标准。比如标准规定如果有多个拥有相同id或者name的元素,只要返回第一个,但chrome和opera会将它们放在一个HTMLCollection或者NodeList里一并返回,参见MDN。
从IDL看不出来的还有如下几点
- 这两个类都是“live”的。对其中元素进行操作,会实时反映到DOM中(也因此如果一次性直接在这类列表上进行多个DOM操作的话,带来的开销会很大)。
item
和namedItem
都可以通过[]
的缩写进行调用,有的浏览器还支持用()
的缩写进行调用(也就是可以list[index]
,list[key]
或者list(index)
,list(key)
),以及直接用dot notation调用namedItem
(比如list.key
)。- 部分浏览器支持对
NodeList
调用namedItem
或间接通过[]
、()
、dot notation来调用namedItem
,但由于各浏览器支持不同,最好不对NodeList
做这种操作。 - IE8及以下版本浏览器中,注释属于
HTMLCommentElement
,算作Element
,因此会出现在HTMLCollection
里。