DOM API详解
来源于:http://zxc0328.github.io/2016/01/23/learning-dom-part1/
https://zxc0328.github.io/2016/01/26/learning-dom-part2/
一、什么是DOM
Document Object Model(DOM)是用编程语言对HTML,XML以及SVG文档进行操作的接口。我们经常使用JavaScript来操纵DOM,不过DOM其实的语言无关的。它并不是JavaScript的一部分。
虽然如此,DOM在前端开发中经常和JS一起讨论,并且对JS造成了极大的负面影响。这个原因就在于早年间各大浏览器厂商(主要是老版本IE),对DOM的实现并没有按W3C的标准来做,而是自己制定了一套接口,由此给前端开发造成了极大的麻烦。我们需要使用jQuery这样的基础库,使用封装好的跨平台兼容API,才能使代码兼容主要浏览器。
直到今天,jQuery这样的基础库依然是不可或缺的。好消息是,随着时代的进步,DOM API的兼容性在主流浏览器中已经不是问题。在没有低版本IE的移动端web中,我们就可以不用为这个问题劳神了。
然而相比于CSS和JS所拥有的明确版本迭代和丰富的文档,DOM无论是在版本的碎片化程度还是文档的数量上来说都很难和前两者相比。
打开MDN的DOM文档首页,我们可以看到密密麻麻的API列表,点开其中的一个,可以看到更多的子API。其中除了DOM Core,有CSS DOM,还有HTMl5相关的DOM,更有Worker这样的浏览器相关的接口。可谓是一个大杂烩,CSS,JS,浏览器各种功能的API都在其中。因此本文就是一个DOM API的分类梳理,带大家看清DOM众多API的组成和层级。
二、DOM标准
我们从W3C的DOM Technical Reports来看,Document Object Model (DOM)目前有Level 1,Level 2,Level 3,以及Level4三个标准。每一个Level的标准下又分为多个模块,其中DOM2 Core Specification的发布时间为2000-11-13,而DOM3 Core Specification的发布时间为2004-04-07,最新的DOM4发布于2015-11-19。
DOM2由许多个标准组成是,如图:
然后我们来看看DOM3。DOM3标准构建在DOM2的基础上,因此数量上少于DOM2的模块。DOM3中的XPath Specification等等标准还没有正式成为推荐标准,因此没有加入图表。
三、DOM2与DOM3,以及非核心API
和CSS标准一样,DOM标准的版本也是各自独立的。我们所说的DOM2,DOM3其实主要指DOM Core Specification的版本。在DOM Core Specification中会写明这个版本的DOM Core和哪些版本的标准一起组成新一代DOM标准。
因此我们也就可以解释为什么DOM3的组成标准要大大少于DOM2了。DOM2中的Style Specification后来发展为了一个新的CSSOM Specification,事实上这个标准已经不属于DOM的范畴,而被划入CSS标准。而Events Specification的Level 3最后发展为了新的UI Events Specification以及Touch Events Specification等等独立的Event Specification,补充了DOM2 Events Specification中缺失的事件。Traversal and Range Specification中的Traversal部分发展为了新的Element Traversal Specification。
具体标准之间的关系可以看下表:
然后我们来看看所谓的非核心API。所谓的非核心API就是那些在W3C标准中不属于DOM的API,但是这些API和DOM的核心部分有着或多或少的关系。如CSS相关的CSSOM Specification,Selectors API等等。以及与HTML5相关的众多JavaScript API,比如Web Storage,Web Workers等等
本文关注的主要是DOM2,DOM3以及部分新模块的API,解读它们的构成,以及在浏览器的兼容情况。
四、API详解-DOM CORE
DOM标准的层级
DOM标准实际由很多的子模块组成,包括Core module,XML module,Events module,User interface Events module,Mouse Events module,Text Events module,Keyboard Events module,Mutation Events module,Mutation name Events module,HTML Events module,Load and Save module,Asynchronous load module,Validation module,和XPath module。
具体的层级结构如下图所示:
image credit: w3.org
DOM Core-导语
在我们把目光转向DOM Core模块之前,我想先讲讲如何阅读DOM的W3C Specification。首先我们可以阅读官方的导语-What is the Document Object Model?。
这一节中介绍了DOM的相关概念。比如关于DOM的结构,我们通常认为是一颗树,但实际上用森林来形容更为贴切,因为虽然以Document
为根节点的文档树最多只能有一颗,但我们还有一颗可选的doctype
节点树,以及comments
节点等等。
更重要的是,导语中还介绍了接下来文档中的一些惯例。比如因为DOM是独立于编程语言的,所以在文档中使用了IDL(Interactive Data Language)来描述接口。然后我们需要注意的一点就是,DOM可以用于HTML和XML,因此在前端同学看来,DOM中会有一些不太熟悉的概念,比如Namespace
和DOM URIs
,这些正是为了兼容XML而加入到DOM中的特性。不用过分在意。
下面是一个简化的IDL示例:
interface Element : Node {
readonly attribute DOMString tagName;
void removeAttribute(in DOMString name)
raises(DOMException);
Attr getAttributeNode(in DOMString name);
NodeList getElementsByTagName(in DOMString name);
};
IDL中主要描述了一个接口的继承关系,以及接口的方法和属性,及相应的参数和返回值
下面我们首先来看DOM Core模块。从“Core”这个名词就可以看出这个模块在整个DOM标准中处于中心地位。DOM Core模块主要定义了DOM最关键的一组接口和对象。其中最顶层的一个接口就是Node
。直接继承Node
的接口有Element
、DocumentType
、Attr
、ProcessingInstruction
、Comment
、Text
、CDATASection
和Notation
。
Node接口
Node
接口是整个DOM中最顶层的接口,DOM中的每一个节点都是一个Node
。从Node
接口分化出其他的更具体的接口。
Node
接口的属性:
属性名 | 返回值 | 备注 |
---|---|---|
nodeName | DOMString | 只读 |
nodeValue | DOMString | 只读 |
nodeType | unsigned short | 只读 |
parentNode | Node | 只读 |
childNodes | NodeList | 只读 |
firstChild | Node | 只读 |
lastChild | Node | 只读 |
previousSibling | Node | 只读 |
nextSibling | Node | 只读 |
attributes | NamedNodeMap | 只读 |
ownerDocument | Document | 只读 |
值得注意的是,虽然这些属性都定义在Node接口上,但是不是所有的底层接口实现都有对应的值。如Text
和Comment
节点都没有子元素,因此它们的childNodes
为null
。同理,Element
接口的nodeValue
值就为null
。
nodeType
属性的值被定义为一组常量:
// NodeType
const unsigned short ELEMENT_NODE = 1;
const unsigned short ATTRIBUTE_NODE = 2;
const unsigned short TEXT_NODE = 3;
const unsigned short CDATA_SECTION_NODE = 4;
const unsigned short ENTITY_REFERENCE_NODE = 5;
const unsigned short ENTITY_NODE = 6;
const unsigned short PROCESSING_INSTRUCTION_NODE = 7;
const unsigned short COMMENT_NODE = 8;
const unsigned short DOCUMENT_NODE = 9;
const unsigned short DOCUMENT_TYPE_NODE = 10;
const unsigned short DOCUMENT_FRAGMENT_NODE = 11;
const unsigned short NOTATION_NODE = 12;
这就是我们平时用来检测节点类型时使用的数值了。
下面从Node
接口的方法中选几个主流的API进行简单的介绍,对于W3C中为标准但在WHATWG中被废弃的,这里不进行介绍(在主流的浏览器中也没有相应的实现)。对于一些在继承Node的接口中有更具体实现的API,这里也不进行介绍(如hasAttributes
在Element接口中有更具体的实现)。
-
compareDocumentPosition
这是DOM3中引入的一个很有意思的API。这个API将两个Node的位置进行比较,然后返回
DocumentPosition
这组常量中的一种。这组常量是:// 节点被另一个节点包含。被包含的节点是父节点的后继 DOCUMENT_POSITION_CONTAINED_BY // 节点包含另一个节点。父节点是子节点的前驱 DOCUMENT_POSITION_CONTAINS DOCUMENT_POSITION_DISCONNECTED // 后继关系 DOCUMENT_POSITION_FOLLOWING DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC // 前驱关系 DOCUMENT_POSITION_PRECEDING
-
appendChild
&&removeChild
这两个API是比较常用的,
appendChild
在DOM子元素列表的最后插入元素,如果这个元素已经存在DOM树中,就先移除这个元素再插入。removeChild
则从DOM子元素列表中移除元素。 -
insertBefore
这个API有两个参数,一个是
newChild
,一个是refChild
。newChild
既是要插入的节点,而refChild
则是一个已经在Node
所在DOM树中的参考子节点。newChild
会插入到refChild
之前,如果refChild
是null
,那么newChild
会插入到Node
子节点列表的末尾。这个API的记忆很简单,命名是
insertBefore
,必然是有一个参考节点的,不然插到谁的前面去呢? -
isEqualNode
这个API的作用是判断两个DOM是否相等。这里的相等并不是指指向同一个对象,而是指属性和值方面是否相同。
对于两个节点来说比较的算法如下:
- 两个节点是否是相同的类型
nodeName, localName, namespaceURI, prefix, nodeValue
这些属性是否相同- 节点的属性集合(在DOM中的数据类型是
NamedNodeMaps
)是否相同,这里集合中属性的顺序可以是随意的 - 子节点集合(
NodeLists
类型)是否相同,这里会考虑节点的index,如果不一样则不相同。
Element接口
Document接口
Attr接口
Text,Comment和CharacterData接口
DOM中的基础数据类型
DOMString
DOMTimeStamp
DOMUserData
DOMObject