1. 概述
1.1 DOM
DOM 是 JavaScript 操作网页的接口,全称为“文档对象模型”(Document Object Model)。它的作用是将网页转为一个 JavaScript 对象,从而可以用脚本进行各种操作(比如增删内容)。
浏览器会根据 DOM 模型,将结构化文档(比如 HTML 和 XML)解析成一系列的节点,再由这些节点组成一个树状结构(DOM Tree)。
严格地说,DOM 不是 JavaScript 语法的一部分,但JavaScript 是最常用于 DOM 操作的语言,离开了 DOM,JavaScript 就无法控制网页。
1.2 节点(node)
DOM 的最小组成单位叫做节点(node)。文档的树形结构(DOM 树),就是由各种不同类型的节点组成。
节点的类型有七种,都继承了原生的节点对象 Node
。
-
Document
:整个文档树的顶层节点 -
DocumentType
:doctype
标签(比如)
-
Element
:网页的各种HTML标签(比如、
等)
-
Attribute
:网页元素的属性(比如class="right"
) -
Text
:标签之间或标签包含的文本 -
Comment
:注释 -
DocumentFragment
:文档的片段
1.3 节点树
文档的第一层只有一个节点,就是 HTML 网页的第一个标签 ,它构成了树结构的根节点(root node),其他 HTML 标签节点都是它的下级节点。
除了根节点,其他节点都有三种层级关系。
- 父节点关系(parentNode):直接的那个上级节点
- 子节点关系(childNodes):直接的下级节点
- 同级节点关系(sibling):拥有同一个父节点的节点
2. Node 接口
所有 DOM 节点对象都继承了 Node 接口,拥有一些共同的属性和方法。
2.1 nodeType 属性
nodeType
属性返回一个整数值,表示节点的类型。
不同节点的 nodeType
属性值和对应的常量如下:
- 元素节点(element):对应常量
Node.ELEMENT_NODE
,1 - 属性节点(attr):常量
Node.ATTRIBUTE_NODE
,2 - 文本节点(text):常量
Node.TEXT_NODE
,3 - 注释节点(Comment):常量
Node.COMMENT_NODE
,8 - 文档节点(document):常量
Node.DOCUMENT_NODE
,9 - 文档类型节点(DocumentType):常量
Node.DOCUMENT_TYPE_NODE
,10 - 文档片断节点(DocumentFragment):常量
Node.DOCUMENT_FRAGMENT_NODE
,11
确定节点类型时,使用 nodeType
属性是常用方法。
2.2 nextSibling 属性
Node.nextSibling
属性返回紧跟在当前节点后面的第一个同级节点。如果当前节点后面没有同级节点,则返回 null
。
// HTML 代码如下
// helloworld
var d1 = document.getElementById('d1');
var d2 = document.getElementById('d2');
d1.nextSibling === d2 // true
注意,该属性还包括文本节点和注释节点()。因此如果当前节点后面有空格,该属性会返回一个文本节点,内容为空格。
2.3 previousSibling 属性
previousSibling
属性返回当前节点前面的、距离最近的一个同级节点。如果当前节点前面没有同级节点,则返回 null
。
// HTML 代码如下
// helloworld
var d1 = document.getElementById('d1');
var d2 = document.getElementById('d2');
d2.previousSibling === d1 // true
注意,该属性还包括文本节点和注释节点。因此如果当前节点前面有空格,该属性会返回一个文本节点,内容为空格。
2.4 父节点 parentNode 和 parentElement 属性
- parentNode 属性
parentNode
属性返回当前节点的父节点。对于一个节点来说,它的父节点只可能是三种类型:元素节点(element)、文档节点(document)和文档片段节点(documentfragment)。
文档节点(document)和文档片段节点(documentfragment)的父节点都是null
。另外,对于那些生成后还没插入 DOM 树的节点,父节点也是null
。 - parentElement 属性
parentElement
属性返回当前节点的父元素节点。如果当前节点没有父节点,或者父节点类型不是元素节点,则返回null
。
注意: Element
:网页的各种HTML标签(比如 、
等)
2.5 childNodes 属性
childNodes
属性返回一个类似数组的对象(NodeList
集合),成员包括当前节点的所有子节点(包括文本节点和注释节点)。
var children = document.querySelector('ul').childNodes;
上面代码中,children
就是 ul
元素的所有子节点。
- 另:关于 ParentNode.children
如果当前节点是父节点,就会继承ParentNode
接口。(只有元素节点(element)、文档节点(document)和文档片段节点(documentFragment)拥有子节点)
children
属性返回一个HTMLCollection
实例,成员是当前节点的所有元素子节点。
注意,children
属性只包括元素子节点,不包括其他类型的子节点(比如文本子节点)
3. NodeList 接口
节点都是单个对象,有时需要一种数据结构,能够容纳多个节点。DOM 提供两种节点集合,用于容纳多个节点:NodeList
和 HTMLCollection
。
主要区别是,NodeList
可以包含各种类型的节点,HTMLCollection
只能包含 HTML
元素节点。
3.1 NodeList 实例
通过以下方法可以得到 NodeList
实例。
Node.childNodes
-
document.querySelectorAll()
等节点搜索方法
document.body.childNodes instanceof NodeList // true
var children = document.body.childNodes;
Array.isArray(children) // false
children.length // 34
children.forEach(console.log)
上面代码中,NodeList
实例 children
不是数组,但是具有 length
属性和 forEach
方法。
3.2 静态/动态集合
NodeList
实例可能是动态集合,也可能是静态集合。
所谓动态集合就是一个活的集合,DOM 删除或新增一个相关节点,都会立刻反映在 NodeList
实例。
目前,只有 Node.childNodes
返回的是一个动态集合,其他的 NodeList
都是静态集合。
var children = document.body.childNodes;
children.length // 18
document.body.appendChild(document.createElement('p'));
children.length // 19
4. HTMLCollection 接口
4.1 概述
HTMLCollection
是一个节点对象的集合,只能包含元素节点(element),不能包含其他类型的节点。
它的返回值是一个类似数组的对象,但是与 NodeList
接口不同,HTMLCollection
没有forEach
方法,只能使用 for
循环遍历。
HTMLCollection
实例都是动态集合,节点的变化会实时反映在集合中。
返回 HTMLCollection
实例的,主要是一些 Document
对象的集合属性,比如 document.links
、document.forms
、document.images
等。
document.links instanceof HTMLCollection // true
4.2 id 或 name 属性引用
如果元素节点有 id
或 name
属性,那么 HTMLCollection
实例上面,可以使用 id
属性或 name
属性引用该节点元素。如果没有对应的节点,则返回 null
。
// HTML 代码如下
//
var pic = document.getElementById('pic');
document.images.pic === pic // true
上面代码中,document.images
是一个HTMLCollection
实例,可以通过元素的
id
属性值,从 HTMLCollection
实例上取到这个元素。
5. ParentNode 接口
节点对象除了继承 Node 接口以外,还会继承其他接口。ParentNode
接口表示当前节点是一个父节点,提供一些处理子节点的方法。ChildNode
接口表示当前节点是一个子节点,提供一些相关方法。
(以下重复文字可看做为:重要的事情多说一遍)
5.1 父节点与 ParentNode 接口
如果当前节点是父节点,就会继承 ParentNode
接口。
由于只有元素节点(element)、文档节点(document)和文档片段节点(documentFragment)拥有子节点,因此只有这三类节点会继承 ParentNode
接口。
5.2 ParentNode.children(只读)
children
属性返回一个 HTMLCollection
实例,成员是当前节点的所有元素子节点。
注意,children
属性只包括元素子节点,不包括其他类型的子节点(比如文本子节点)。如果没有元素类型的子节点,返回值 HTMLCollection
实例的 length
属性为 0
。
另外,HTMLCollection
是动态集合,会实时反映 DOM 的任何变化。
6. ChildNode 接口
6.1 概述
ChildNode
接口表示当前节点是一个子节点,提供一些相关方法。
如果一个节点有父节点,那么该节点就继承了 ChildNode
接口。
6.2 方法
- ChildNode.remove()
remove
方法用于从父节点移除当前节点。
el.remove()
上面代码在 DOM 里面移除了 el
节点。
- ChildNode.before()
before
方法用于在当前节点的前面,插入一个或多个同级节点。两者拥有相同的父节点。
该方法不仅可以插入元素节点,还可以插入文本节点。
var p = document.createElement('p');
var p1 = document.createElement('p');
// 插入元素节点
el.before(p);
// 插入文本节点
el.before('Hello');
// 插入多个元素节点
el.before(p, p1);
// 插入元素节点和文本节点
el.before(p, 'Hello');
ChildNode.after()
after
方法用于在当前节点的后面,插入一个或多个同级节点,两者拥有相同的父节点。用法与before
方法完全相同。ChildNode.replaceWith()
replaceWith
方法使用参数节点,替换当前节点。参数可以是元素节点,也可以是文本节点。
var span = document.createElement('span');
el.replaceWith(span);
上面代码中,el
节点将被 span
节点替换。
参考连接
- 阮一峰, JavaScript 教程